mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-05-02 16:15:59 +08:00
refactor: db refactor and cache key add. (#2320)
* refactor: db refactor and cache key add. * refactor: db refactor and cache key add. * refactor: go version update. * refactor: file name change.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/tokenverify"
|
||||
)
|
||||
|
||||
type AuthDatabase interface {
|
||||
// If the result is empty, no error is returned.
|
||||
GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error)
|
||||
// Create token
|
||||
CreateToken(ctx context.Context, userID string, platformID int) (string, error)
|
||||
|
||||
SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error
|
||||
}
|
||||
|
||||
type authDatabase struct {
|
||||
cache cache.TokenModel
|
||||
accessSecret string
|
||||
accessExpire int64
|
||||
}
|
||||
|
||||
func NewAuthDatabase(cache cache.TokenModel, accessSecret string, accessExpire int64) AuthDatabase {
|
||||
return &authDatabase{cache: cache, accessSecret: accessSecret, accessExpire: accessExpire}
|
||||
}
|
||||
|
||||
// If the result is empty.
|
||||
func (a *authDatabase) GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) {
|
||||
return a.cache.GetTokensWithoutError(ctx, userID, platformID)
|
||||
}
|
||||
|
||||
func (a *authDatabase) SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error {
|
||||
return a.cache.SetTokenMapByUidPid(ctx, userID, platformID, m)
|
||||
}
|
||||
|
||||
// Create Token.
|
||||
func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformID int) (string, error) {
|
||||
tokens, err := a.cache.GetTokensWithoutError(ctx, userID, platformID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var deleteTokenKey []string
|
||||
for k, v := range tokens {
|
||||
_, err = tokenverify.GetClaimFromToken(k, authverify.Secret(a.accessSecret))
|
||||
if err != nil || v != constant.NormalToken {
|
||||
deleteTokenKey = append(deleteTokenKey, k)
|
||||
}
|
||||
}
|
||||
if len(deleteTokenKey) != 0 {
|
||||
err = a.cache.DeleteTokenByUidPid(ctx, userID, platformID, deleteTokenKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
claims := tokenverify.BuildClaims(userID, platformID, a.accessExpire)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString([]byte(a.accessSecret))
|
||||
if err != nil {
|
||||
return "", errs.WrapMsg(err, "token.SignedString")
|
||||
}
|
||||
return tokenString, a.cache.AddTokenFlag(ctx, userID, platformID, tokenString, constant.NormalToken)
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
type BlackDatabase interface {
|
||||
// Create add BlackList
|
||||
Create(ctx context.Context, blacks []*model.Black) (err error)
|
||||
// Delete delete BlackList
|
||||
Delete(ctx context.Context, blacks []*model.Black) (err error)
|
||||
// FindOwnerBlacks get BlackList list
|
||||
FindOwnerBlacks(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, blacks []*model.Black, err error)
|
||||
FindBlackInfos(ctx context.Context, ownerUserID string, userIDs []string) (blacks []*model.Black, err error)
|
||||
// CheckIn Check whether user2 is in the black list of user1 (inUser1Blacks==true) Check whether user1 is in the black list of user2 (inUser2Blacks==true)
|
||||
CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Blacks bool, inUser2Blacks bool, err error)
|
||||
}
|
||||
|
||||
type blackDatabase struct {
|
||||
black database.Black
|
||||
cache cache.BlackCache
|
||||
}
|
||||
|
||||
func NewBlackDatabase(black database.Black, cache cache.BlackCache) BlackDatabase {
|
||||
return &blackDatabase{black, cache}
|
||||
}
|
||||
|
||||
// Create Add Blacklist.
|
||||
func (b *blackDatabase) Create(ctx context.Context, blacks []*model.Black) (err error) {
|
||||
if err := b.black.Create(ctx, blacks); err != nil {
|
||||
return err
|
||||
}
|
||||
return b.deleteBlackIDsCache(ctx, blacks)
|
||||
}
|
||||
|
||||
// Delete Delete Blacklist.
|
||||
func (b *blackDatabase) Delete(ctx context.Context, blacks []*model.Black) (err error) {
|
||||
if err := b.black.Delete(ctx, blacks); err != nil {
|
||||
return err
|
||||
}
|
||||
return b.deleteBlackIDsCache(ctx, blacks)
|
||||
}
|
||||
|
||||
// FindOwnerBlacks Get Blacklist List.
|
||||
func (b *blackDatabase) deleteBlackIDsCache(ctx context.Context, blacks []*model.Black) (err error) {
|
||||
cache := b.cache.CloneBlackCache()
|
||||
for _, black := range blacks {
|
||||
cache = cache.DelBlackIDs(ctx, black.OwnerUserID)
|
||||
}
|
||||
return cache.ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
// FindOwnerBlacks Get Blacklist List.
|
||||
func (b *blackDatabase) FindOwnerBlacks(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, blacks []*model.Black, err error) {
|
||||
return b.black.FindOwnerBlacks(ctx, ownerUserID, pagination)
|
||||
}
|
||||
|
||||
// FindOwnerBlacks Get Blacklist List.
|
||||
func (b *blackDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Blacks bool, inUser2Blacks bool, err error) {
|
||||
userID1BlackIDs, err := b.cache.GetBlackIDs(ctx, userID1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userID2BlackIDs, err := b.cache.GetBlackIDs(ctx, userID2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.ZDebug(ctx, "blackIDs", "user1BlackIDs", userID1BlackIDs, "user2BlackIDs", userID2BlackIDs)
|
||||
return datautil.Contain(userID2, userID1BlackIDs...), datautil.Contain(userID1, userID2BlackIDs...), nil
|
||||
}
|
||||
|
||||
// FindBlackIDs Get Blacklist List.
|
||||
func (b *blackDatabase) FindBlackIDs(ctx context.Context, ownerUserID string) (blackIDs []string, err error) {
|
||||
return b.cache.GetBlackIDs(ctx, ownerUserID)
|
||||
}
|
||||
|
||||
// FindBlackInfos Get Blacklist List.
|
||||
func (b *blackDatabase) FindBlackInfos(ctx context.Context, ownerUserID string, userIDs []string) (blacks []*model.Black, err error) {
|
||||
return b.black.FindOwnerBlackInfos(ctx, ownerUserID, userIDs)
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/db/tx"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
)
|
||||
|
||||
type ConversationDatabase interface {
|
||||
// UpdateUsersConversationField updates the properties of a conversation for specified users.
|
||||
UpdateUsersConversationField(ctx context.Context, userIDs []string, conversationID string, args map[string]any) error
|
||||
// CreateConversation creates a batch of new conversations.
|
||||
CreateConversation(ctx context.Context, conversations []*relationtb.Conversation) error
|
||||
// SyncPeerUserPrivateConversationTx ensures transactional operation while syncing private conversations between peers.
|
||||
SyncPeerUserPrivateConversationTx(ctx context.Context, conversation []*relationtb.Conversation) error
|
||||
// FindConversations retrieves multiple conversations of a user by conversation IDs.
|
||||
FindConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationtb.Conversation, error)
|
||||
// GetUserAllConversation fetches all conversations of a user on the server.
|
||||
GetUserAllConversation(ctx context.Context, ownerUserID string) ([]*relationtb.Conversation, error)
|
||||
// SetUserConversations sets multiple conversation properties for a user, creates new conversations if they do not exist, or updates them otherwise. This operation is atomic.
|
||||
SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationtb.Conversation) error
|
||||
// SetUsersConversationFieldTx updates a specific field for multiple users' conversations, creating new conversations if they do not exist, or updates them otherwise. This operation is
|
||||
// transactional.
|
||||
SetUsersConversationFieldTx(ctx context.Context, userIDs []string, conversation *relationtb.Conversation, fieldMap map[string]any) error
|
||||
// CreateGroupChatConversation creates a group chat conversation for the specified group ID and user IDs.
|
||||
CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error
|
||||
// GetConversationIDs retrieves conversation IDs for a given user.
|
||||
GetConversationIDs(ctx context.Context, userID string) ([]string, error)
|
||||
// GetUserConversationIDsHash gets the hash of conversation IDs for a given user.
|
||||
GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error)
|
||||
// GetAllConversationIDs fetches all conversation IDs.
|
||||
GetAllConversationIDs(ctx context.Context) ([]string, error)
|
||||
// GetAllConversationIDsNumber returns the number of all conversation IDs.
|
||||
GetAllConversationIDsNumber(ctx context.Context) (int64, error)
|
||||
// PageConversationIDs paginates through conversation IDs based on the specified pagination settings.
|
||||
PageConversationIDs(ctx context.Context, pagination pagination.Pagination) (conversationIDs []string, err error)
|
||||
// GetConversationsByConversationID retrieves conversations by their IDs.
|
||||
GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationtb.Conversation, error)
|
||||
// GetConversationIDsNeedDestruct fetches conversations that need to be destructed based on specific criteria.
|
||||
GetConversationIDsNeedDestruct(ctx context.Context) ([]*relationtb.Conversation, error)
|
||||
// GetConversationNotReceiveMessageUserIDs gets user IDs for users in a conversation who have not received messages.
|
||||
GetConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) ([]string, error)
|
||||
// GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error)
|
||||
// FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error)
|
||||
}
|
||||
|
||||
func NewConversationDatabase(conversation database.Conversation, cache cache.ConversationCache, tx tx.Tx) ConversationDatabase {
|
||||
return &conversationDatabase{
|
||||
conversationDB: conversation,
|
||||
cache: cache,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
type conversationDatabase struct {
|
||||
conversationDB database.Conversation
|
||||
cache cache.ConversationCache
|
||||
tx tx.Tx
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) SetUsersConversationFieldTx(ctx context.Context, userIDs []string, conversation *relationtb.Conversation, fieldMap map[string]any) (err error) {
|
||||
return c.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
cache := c.cache.CloneConversationCache()
|
||||
if conversation.GroupID != "" {
|
||||
cache = cache.DelSuperGroupRecvMsgNotNotifyUserIDs(conversation.GroupID).DelSuperGroupRecvMsgNotNotifyUserIDsHash(conversation.GroupID)
|
||||
}
|
||||
haveUserIDs, err := c.conversationDB.FindUserID(ctx, userIDs, []string{conversation.ConversationID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(haveUserIDs) > 0 {
|
||||
_, err = c.conversationDB.UpdateByMap(ctx, haveUserIDs, conversation.ConversationID, fieldMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache = cache.DelUsersConversation(conversation.ConversationID, haveUserIDs...)
|
||||
if _, ok := fieldMap["has_read_seq"]; ok {
|
||||
for _, userID := range haveUserIDs {
|
||||
cache = cache.DelUserAllHasReadSeqs(userID, conversation.ConversationID)
|
||||
}
|
||||
}
|
||||
if _, ok := fieldMap["recv_msg_opt"]; ok {
|
||||
cache = cache.DelConversationNotReceiveMessageUserIDs(conversation.ConversationID)
|
||||
}
|
||||
}
|
||||
NotUserIDs := stringutil.DifferenceString(haveUserIDs, userIDs)
|
||||
log.ZDebug(ctx, "SetUsersConversationFieldTx", "NotUserIDs", NotUserIDs, "haveUserIDs", haveUserIDs, "userIDs", userIDs)
|
||||
var conversations []*relationtb.Conversation
|
||||
now := time.Now()
|
||||
for _, v := range NotUserIDs {
|
||||
temp := new(relationtb.Conversation)
|
||||
if err = datautil.CopyStructFields(temp, conversation); err != nil {
|
||||
return err
|
||||
}
|
||||
temp.OwnerUserID = v
|
||||
temp.CreateTime = now
|
||||
conversations = append(conversations, temp)
|
||||
}
|
||||
if len(conversations) > 0 {
|
||||
err = c.conversationDB.Create(ctx, conversations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache = cache.DelConversationIDs(NotUserIDs...).DelUserConversationIDsHash(NotUserIDs...).DelConversations(conversation.ConversationID, NotUserIDs...)
|
||||
}
|
||||
return cache.ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) UpdateUsersConversationField(ctx context.Context, userIDs []string, conversationID string, args map[string]any) error {
|
||||
_, err := c.conversationDB.UpdateByMap(ctx, userIDs, conversationID, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache := c.cache.CloneConversationCache()
|
||||
cache = cache.DelUsersConversation(conversationID, userIDs...)
|
||||
if _, ok := args["recv_msg_opt"]; ok {
|
||||
cache = cache.DelConversationNotReceiveMessageUserIDs(conversationID)
|
||||
}
|
||||
return cache.ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) CreateConversation(ctx context.Context, conversations []*relationtb.Conversation) error {
|
||||
if err := c.conversationDB.Create(ctx, conversations); err != nil {
|
||||
return err
|
||||
}
|
||||
var userIDs []string
|
||||
cache := c.cache.CloneConversationCache()
|
||||
for _, conversation := range conversations {
|
||||
cache = cache.DelConversations(conversation.OwnerUserID, conversation.ConversationID)
|
||||
cache = cache.DelConversationNotReceiveMessageUserIDs(conversation.ConversationID)
|
||||
userIDs = append(userIDs, conversation.OwnerUserID)
|
||||
}
|
||||
return cache.DelConversationIDs(userIDs...).DelUserConversationIDsHash(userIDs...).ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) SyncPeerUserPrivateConversationTx(ctx context.Context, conversations []*relationtb.Conversation) error {
|
||||
return c.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
cache := c.cache.CloneConversationCache()
|
||||
for _, conversation := range conversations {
|
||||
for _, v := range [][2]string{{conversation.OwnerUserID, conversation.UserID}, {conversation.UserID, conversation.OwnerUserID}} {
|
||||
ownerUserID := v[0]
|
||||
userID := v[1]
|
||||
haveUserIDs, err := c.conversationDB.FindUserID(ctx, []string{ownerUserID}, []string{conversation.ConversationID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(haveUserIDs) > 0 {
|
||||
_, err := c.conversationDB.UpdateByMap(ctx, []string{ownerUserID}, conversation.ConversationID, map[string]any{"is_private_chat": conversation.IsPrivateChat})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache = cache.DelUsersConversation(conversation.ConversationID, ownerUserID)
|
||||
} else {
|
||||
newConversation := *conversation
|
||||
newConversation.OwnerUserID = ownerUserID
|
||||
newConversation.UserID = userID
|
||||
newConversation.ConversationID = conversation.ConversationID
|
||||
newConversation.IsPrivateChat = conversation.IsPrivateChat
|
||||
if err := c.conversationDB.Create(ctx, []*relationtb.Conversation{&newConversation}); err != nil {
|
||||
return err
|
||||
}
|
||||
cache = cache.DelConversationIDs(ownerUserID).DelUserConversationIDsHash(ownerUserID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache.ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) FindConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationtb.Conversation, error) {
|
||||
return c.cache.GetConversations(ctx, ownerUserID, conversationIDs)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetConversation(ctx context.Context, ownerUserID string, conversationID string) (*relationtb.Conversation, error) {
|
||||
return c.cache.GetConversation(ctx, ownerUserID, conversationID)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetUserAllConversation(ctx context.Context, ownerUserID string) ([]*relationtb.Conversation, error) {
|
||||
return c.cache.GetUserAllConversations(ctx, ownerUserID)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationtb.Conversation) error {
|
||||
return c.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
cache := c.cache.CloneConversationCache()
|
||||
groupIDs := datautil.Distinct(datautil.Filter(conversations, func(e *relationtb.Conversation) (string, bool) {
|
||||
return e.GroupID, e.GroupID != ""
|
||||
}))
|
||||
for _, groupID := range groupIDs {
|
||||
cache = cache.DelSuperGroupRecvMsgNotNotifyUserIDs(groupID).DelSuperGroupRecvMsgNotNotifyUserIDsHash(groupID)
|
||||
}
|
||||
var conversationIDs []string
|
||||
for _, conversation := range conversations {
|
||||
conversationIDs = append(conversationIDs, conversation.ConversationID)
|
||||
cache = cache.DelConversations(conversation.OwnerUserID, conversation.ConversationID)
|
||||
}
|
||||
existConversations, err := c.conversationDB.Find(ctx, ownerUserID, conversationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(existConversations) > 0 {
|
||||
for _, conversation := range conversations {
|
||||
err = c.conversationDB.Update(ctx, conversation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
var existConversationIDs []string
|
||||
for _, conversation := range existConversations {
|
||||
existConversationIDs = append(existConversationIDs, conversation.ConversationID)
|
||||
}
|
||||
|
||||
var notExistConversations []*relationtb.Conversation
|
||||
for _, conversation := range conversations {
|
||||
if !datautil.Contain(conversation.ConversationID, existConversationIDs...) {
|
||||
notExistConversations = append(notExistConversations, conversation)
|
||||
}
|
||||
}
|
||||
if len(notExistConversations) > 0 {
|
||||
err = c.conversationDB.Create(ctx, notExistConversations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache = cache.DelConversationIDs(ownerUserID).
|
||||
DelUserConversationIDsHash(ownerUserID).
|
||||
DelConversationNotReceiveMessageUserIDs(datautil.Slice(notExistConversations, func(e *relationtb.Conversation) string { return e.ConversationID })...)
|
||||
}
|
||||
return cache.ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// func (c *conversationDatabase) FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) {
|
||||
// return c.cache.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID)
|
||||
//}
|
||||
|
||||
func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error {
|
||||
return c.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
cache := c.cache.CloneConversationCache()
|
||||
conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, groupID)
|
||||
existConversationUserIDs, err := c.conversationDB.FindUserID(ctx, userIDs, []string{conversationID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
notExistUserIDs := stringutil.DifferenceString(userIDs, existConversationUserIDs)
|
||||
var conversations []*relationtb.Conversation
|
||||
for _, v := range notExistUserIDs {
|
||||
conversation := relationtb.Conversation{ConversationType: constant.ReadGroupChatType, GroupID: groupID, OwnerUserID: v, ConversationID: conversationID}
|
||||
conversations = append(conversations, &conversation)
|
||||
cache = cache.DelConversations(v, conversationID).DelConversationNotReceiveMessageUserIDs(conversationID)
|
||||
}
|
||||
cache = cache.DelConversationIDs(notExistUserIDs...).DelUserConversationIDsHash(notExistUserIDs...)
|
||||
if len(conversations) > 0 {
|
||||
err = c.conversationDB.Create(ctx, conversations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = c.conversationDB.UpdateByMap(ctx, existConversationUserIDs, conversationID, map[string]any{"max_seq": 0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range existConversationUserIDs {
|
||||
cache = cache.DelConversations(v, conversationID)
|
||||
}
|
||||
return cache.ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetConversationIDs(ctx context.Context, userID string) ([]string, error) {
|
||||
return c.cache.GetUserConversationIDs(ctx, userID)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) {
|
||||
return c.cache.GetUserConversationIDsHash(ctx, ownerUserID)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetAllConversationIDs(ctx context.Context) ([]string, error) {
|
||||
return c.conversationDB.GetAllConversationIDs(ctx)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetAllConversationIDsNumber(ctx context.Context) (int64, error) {
|
||||
return c.conversationDB.GetAllConversationIDsNumber(ctx)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) PageConversationIDs(ctx context.Context, pagination pagination.Pagination) ([]string, error) {
|
||||
return c.conversationDB.PageConversationIDs(ctx, pagination)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationtb.Conversation, error) {
|
||||
return c.conversationDB.GetConversationsByConversationID(ctx, conversationIDs)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetConversationIDsNeedDestruct(ctx context.Context) ([]*relationtb.Conversation, error) {
|
||||
return c.conversationDB.GetConversationIDsNeedDestruct(ctx)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) ([]string, error) {
|
||||
return c.cache.GetConversationNotReceiveMessageUserIDs(ctx, conversationID)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright © 2024 OpenIM. 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 controller // import "github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
|
||||
@@ -0,0 +1,348 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/db/tx"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
type FriendDatabase interface {
|
||||
// CheckIn checks if user2 is in user1's friend list (inUser1Friends==true) and if user1 is in user2's friend list (inUser2Friends==true)
|
||||
CheckIn(ctx context.Context, user1, user2 string) (inUser1Friends bool, inUser2Friends bool, err error)
|
||||
|
||||
// AddFriendRequest adds or updates a friend request
|
||||
AddFriendRequest(ctx context.Context, fromUserID, toUserID string, reqMsg string, ex string) (err error)
|
||||
|
||||
// BecomeFriends first checks if the users are already in the friends model; if not, it inserts them as friends
|
||||
BecomeFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, addSource int32) (err error)
|
||||
|
||||
// RefuseFriendRequest refuses a friend request
|
||||
RefuseFriendRequest(ctx context.Context, friendRequest *model.FriendRequest) (err error)
|
||||
|
||||
// AgreeFriendRequest accepts a friend request
|
||||
AgreeFriendRequest(ctx context.Context, friendRequest *model.FriendRequest) (err error)
|
||||
|
||||
// Delete removes a friend or friends from the owner's friend list
|
||||
Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) (err error)
|
||||
|
||||
// UpdateRemark updates the remark for a friend
|
||||
UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error)
|
||||
|
||||
// PageOwnerFriends retrieves the friend list of ownerUserID with pagination
|
||||
PageOwnerFriends(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, friends []*model.Friend, err error)
|
||||
|
||||
// PageInWhoseFriends finds the users who have friendUserID in their friend list with pagination
|
||||
PageInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*model.Friend, err error)
|
||||
|
||||
// PageFriendRequestFromMe retrieves the friend requests sent by the user with pagination
|
||||
PageFriendRequestFromMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error)
|
||||
|
||||
// PageFriendRequestToMe retrieves the friend requests received by the user with pagination
|
||||
PageFriendRequestToMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error)
|
||||
|
||||
// FindFriendsWithError fetches specified friends of a user and returns an error if any do not exist
|
||||
FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*model.Friend, err error)
|
||||
|
||||
// FindFriendUserIDs retrieves the friend IDs of a user
|
||||
FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error)
|
||||
|
||||
// FindBothFriendRequests finds friend requests sent and received
|
||||
FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*model.FriendRequest, err error)
|
||||
|
||||
// UpdateFriends updates fields for friends
|
||||
UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error)
|
||||
}
|
||||
|
||||
type friendDatabase struct {
|
||||
friend database.Friend
|
||||
friendRequest database.FriendRequest
|
||||
tx tx.Tx
|
||||
cache cache.FriendCache
|
||||
}
|
||||
|
||||
func NewFriendDatabase(friend database.Friend, friendRequest database.FriendRequest, cache cache.FriendCache, tx tx.Tx) FriendDatabase {
|
||||
return &friendDatabase{friend: friend, friendRequest: friendRequest, cache: cache, tx: tx}
|
||||
}
|
||||
|
||||
// CheckIn verifies if user2 is in user1's friend list (inUser1Friends returns true) and
|
||||
// if user1 is in user2's friend list (inUser2Friends returns true).
|
||||
func (f *friendDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Friends bool, inUser2Friends bool, err error) {
|
||||
// Retrieve friend IDs of userID1 from the cache
|
||||
userID1FriendIDs, err := f.cache.GetFriendIDs(ctx, userID1)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error retrieving friend IDs for user %s: %w", userID1, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve friend IDs of userID2 from the cache
|
||||
userID2FriendIDs, err := f.cache.GetFriendIDs(ctx, userID2)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error retrieving friend IDs for user %s: %w", userID2, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if userID2 is in userID1's friend list and vice versa
|
||||
inUser1Friends = datautil.Contain(userID2, userID1FriendIDs...)
|
||||
inUser2Friends = datautil.Contain(userID1, userID2FriendIDs...)
|
||||
return inUser1Friends, inUser2Friends, nil
|
||||
}
|
||||
|
||||
// AddFriendRequest adds or updates a friend request.
|
||||
func (f *friendDatabase) AddFriendRequest(ctx context.Context, fromUserID, toUserID string, reqMsg string, ex string) (err error) {
|
||||
return f.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
_, err := f.friendRequest.Take(ctx, fromUserID, toUserID)
|
||||
switch {
|
||||
case err == nil:
|
||||
m := make(map[string]any, 1)
|
||||
m["handle_result"] = 0
|
||||
m["handle_msg"] = ""
|
||||
m["req_msg"] = reqMsg
|
||||
m["ex"] = ex
|
||||
m["create_time"] = time.Now()
|
||||
return f.friendRequest.UpdateByMap(ctx, fromUserID, toUserID, m)
|
||||
case mgo.IsNotFound(err):
|
||||
return f.friendRequest.Create(
|
||||
ctx,
|
||||
[]*model.FriendRequest{{FromUserID: fromUserID, ToUserID: toUserID, ReqMsg: reqMsg, Ex: ex, CreateTime: time.Now(), HandleTime: time.Unix(0, 0)}},
|
||||
)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// (1) First determine whether it is in the friends list (in or out does not return an error) (2) for not in the friends list can be inserted.
|
||||
func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, addSource int32) (err error) {
|
||||
return f.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
cache := f.cache.CloneFriendCache()
|
||||
// user find friends
|
||||
fs1, err := f.friend.FindFriends(ctx, ownerUserID, friendUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opUserID := mcontext.GetOperationID(ctx)
|
||||
for _, v := range friendUserIDs {
|
||||
fs1 = append(fs1, &model.Friend{OwnerUserID: ownerUserID, FriendUserID: v, AddSource: addSource, OperatorUserID: opUserID})
|
||||
}
|
||||
fs11 := datautil.DistinctAny(fs1, func(e *model.Friend) string {
|
||||
return e.FriendUserID
|
||||
})
|
||||
|
||||
err = f.friend.Create(ctx, fs11)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fs2, err := f.friend.FindReversalFriends(ctx, ownerUserID, friendUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var newFriendIDs []string
|
||||
for _, v := range friendUserIDs {
|
||||
fs2 = append(fs2, &model.Friend{OwnerUserID: v, FriendUserID: ownerUserID, AddSource: addSource, OperatorUserID: opUserID})
|
||||
newFriendIDs = append(newFriendIDs, v)
|
||||
}
|
||||
fs22 := datautil.DistinctAny(fs2, func(e *model.Friend) string {
|
||||
return e.OwnerUserID
|
||||
})
|
||||
err = f.friend.Create(ctx, fs22)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newFriendIDs = append(newFriendIDs, ownerUserID)
|
||||
cache = cache.DelFriendIDs(newFriendIDs...)
|
||||
return cache.ChainExecDel(ctx)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// RefuseFriendRequest rejects a friend request. It first checks for an existing, unprocessed request.
|
||||
// If no such request exists, it returns an error. Otherwise, it marks the request as refused.
|
||||
func (f *friendDatabase) RefuseFriendRequest(ctx context.Context, friendRequest *model.FriendRequest) error {
|
||||
// Attempt to retrieve the friend request from the database.
|
||||
fr, err := f.friendRequest.Take(ctx, friendRequest.FromUserID, friendRequest.ToUserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve friend request from %s to %s: %w", friendRequest.FromUserID, friendRequest.ToUserID, err)
|
||||
}
|
||||
|
||||
// Check if the friend request has already been handled.
|
||||
if fr.HandleResult != 0 {
|
||||
return fmt.Errorf("friend request from %s to %s has already been processed", friendRequest.FromUserID, friendRequest.ToUserID)
|
||||
}
|
||||
|
||||
// Log the action of refusing the friend request for debugging and auditing purposes.
|
||||
log.ZDebug(ctx, "Refusing friend request", map[string]interface{}{
|
||||
"DB_FriendRequest": fr,
|
||||
"Arg_FriendRequest": friendRequest,
|
||||
})
|
||||
|
||||
// Mark the friend request as refused and update the handle time.
|
||||
friendRequest.HandleResult = constant.FriendResponseRefuse
|
||||
friendRequest.HandleTime = time.Now()
|
||||
if err := f.friendRequest.Update(ctx, friendRequest); err != nil {
|
||||
return fmt.Errorf("failed to update friend request from %s to %s as refused: %w", friendRequest.FromUserID, friendRequest.ToUserID, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AgreeFriendRequest accepts a friend request. It first checks for an existing, unprocessed request.
|
||||
func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest *model.FriendRequest) (err error) {
|
||||
return f.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
now := time.Now()
|
||||
fr, err := f.friendRequest.Take(ctx, friendRequest.FromUserID, friendRequest.ToUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fr.HandleResult != 0 {
|
||||
return errs.ErrArgs.WrapMsg("the friend request has been processed")
|
||||
}
|
||||
friendRequest.HandlerUserID = mcontext.GetOpUserID(ctx)
|
||||
friendRequest.HandleResult = constant.FriendResponseAgree
|
||||
friendRequest.HandleTime = now
|
||||
err = f.friendRequest.Update(ctx, friendRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fr2, err := f.friendRequest.Take(ctx, friendRequest.ToUserID, friendRequest.FromUserID)
|
||||
if err == nil && fr2.HandleResult == constant.FriendResponseNotHandle {
|
||||
fr2.HandlerUserID = mcontext.GetOpUserID(ctx)
|
||||
fr2.HandleResult = constant.FriendResponseAgree
|
||||
fr2.HandleTime = now
|
||||
err = f.friendRequest.Update(ctx, fr2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil && (!mgo.IsNotFound(err)) {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := f.friend.FindUserState(ctx, friendRequest.FromUserID, friendRequest.ToUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
existsMap := datautil.SliceSet(datautil.Slice(exists, func(friend *model.Friend) [2]string {
|
||||
return [...]string{friend.OwnerUserID, friend.FriendUserID} // My - Friend
|
||||
}))
|
||||
var adds []*model.Friend
|
||||
if _, ok := existsMap[[...]string{friendRequest.ToUserID, friendRequest.FromUserID}]; !ok { // My - Friend
|
||||
adds = append(
|
||||
adds,
|
||||
&model.Friend{
|
||||
OwnerUserID: friendRequest.ToUserID,
|
||||
FriendUserID: friendRequest.FromUserID,
|
||||
AddSource: int32(constant.BecomeFriendByApply),
|
||||
OperatorUserID: friendRequest.FromUserID,
|
||||
},
|
||||
)
|
||||
}
|
||||
if _, ok := existsMap[[...]string{friendRequest.FromUserID, friendRequest.ToUserID}]; !ok { // My - Friend
|
||||
adds = append(
|
||||
adds,
|
||||
&model.Friend{
|
||||
OwnerUserID: friendRequest.FromUserID,
|
||||
FriendUserID: friendRequest.ToUserID,
|
||||
AddSource: int32(constant.BecomeFriendByApply),
|
||||
OperatorUserID: friendRequest.FromUserID,
|
||||
},
|
||||
)
|
||||
}
|
||||
if len(adds) > 0 {
|
||||
if err := f.friend.Create(ctx, adds); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return f.cache.DelFriendIDs(friendRequest.ToUserID, friendRequest.FromUserID).ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// Delete removes a friend relationship. It is assumed that the external caller has verified the friendship status.
|
||||
func (f *friendDatabase) Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) (err error) {
|
||||
if err := f.friend.Delete(ctx, ownerUserID, friendUserIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.cache.DelFriendIDs(append(friendUserIDs, ownerUserID)...).ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
// UpdateRemark updates the remark for a friend. Zero value for remark is also supported.
|
||||
func (f *friendDatabase) UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error) {
|
||||
if err := f.friend.UpdateRemark(ctx, ownerUserID, friendUserID, remark); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.cache.DelFriend(ownerUserID, friendUserID).ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
// PageOwnerFriends retrieves the list of friends for the ownerUserID. It does not return an error if the result is empty.
|
||||
func (f *friendDatabase) PageOwnerFriends(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, friends []*model.Friend, err error) {
|
||||
return f.friend.FindOwnerFriends(ctx, ownerUserID, pagination)
|
||||
}
|
||||
|
||||
// PageInWhoseFriends identifies in whose friend lists the friendUserID appears.
|
||||
func (f *friendDatabase) PageInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*model.Friend, err error) {
|
||||
return f.friend.FindInWhoseFriends(ctx, friendUserID, pagination)
|
||||
}
|
||||
|
||||
// PageFriendRequestFromMe retrieves friend requests sent by me. It does not return an error if the result is empty.
|
||||
func (f *friendDatabase) PageFriendRequestFromMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error) {
|
||||
return f.friendRequest.FindFromUserID(ctx, userID, pagination)
|
||||
}
|
||||
|
||||
// PageFriendRequestToMe retrieves friend requests received by me. It does not return an error if the result is empty.
|
||||
func (f *friendDatabase) PageFriendRequestToMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error) {
|
||||
return f.friendRequest.FindToUserID(ctx, userID, pagination)
|
||||
}
|
||||
|
||||
// FindFriendsWithError retrieves specified friends' information for ownerUserID. Returns an error if any friend does not exist.
|
||||
func (f *friendDatabase) FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*model.Friend, err error) {
|
||||
friends, err = f.friend.FindFriends(ctx, ownerUserID, friendUserIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(friends) != len(friendUserIDs) {
|
||||
err = errs.ErrRecordNotFound.Wrap()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *friendDatabase) FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) {
|
||||
return f.cache.GetFriendIDs(ctx, ownerUserID)
|
||||
}
|
||||
|
||||
func (f *friendDatabase) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*model.FriendRequest, err error) {
|
||||
return f.friendRequest.FindBothFriendRequests(ctx, fromUserID, toUserID)
|
||||
}
|
||||
func (f *friendDatabase) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error) {
|
||||
if len(val) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := f.friend.UpdateFriends(ctx, ownerUserID, friendUserIDs, val); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.cache.DelFriends(ownerUserID, friendUserIDs).ChainExecDel(ctx)
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
redis2 "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/common"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/db/tx"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type GroupDatabase interface {
|
||||
// CreateGroup creates new groups along with their members.
|
||||
CreateGroup(ctx context.Context, groups []*model.Group, groupMembers []*model.GroupMember) error
|
||||
// TakeGroup retrieves a single group by its ID.
|
||||
TakeGroup(ctx context.Context, groupID string) (group *model.Group, err error)
|
||||
// FindGroup retrieves multiple groups by their IDs.
|
||||
FindGroup(ctx context.Context, groupIDs []string) (groups []*model.Group, err error)
|
||||
// SearchGroup searches for groups based on a keyword and pagination settings, returns total count and groups.
|
||||
SearchGroup(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*model.Group, error)
|
||||
// UpdateGroup updates the properties of a group identified by its ID.
|
||||
UpdateGroup(ctx context.Context, groupID string, data map[string]any) error
|
||||
// DismissGroup disbands a group and optionally removes its members based on the deleteMember flag.
|
||||
DismissGroup(ctx context.Context, groupID string, deleteMember bool) error
|
||||
|
||||
// TakeGroupMember retrieves a specific group member by group ID and user ID.
|
||||
TakeGroupMember(ctx context.Context, groupID string, userID string) (groupMember *model.GroupMember, err error)
|
||||
// TakeGroupOwner retrieves the owner of a group by group ID.
|
||||
TakeGroupOwner(ctx context.Context, groupID string) (*model.GroupMember, error)
|
||||
// FindGroupMembers retrieves members of a group filtered by user IDs.
|
||||
FindGroupMembers(ctx context.Context, groupID string, userIDs []string) (groupMembers []*model.GroupMember, err error)
|
||||
// FindGroupMemberUser retrieves groups that a user is a member of, filtered by group IDs.
|
||||
FindGroupMemberUser(ctx context.Context, groupIDs []string, userID string) (groupMembers []*model.GroupMember, err error)
|
||||
// FindGroupMemberRoleLevels retrieves group members filtered by their role levels within a group.
|
||||
FindGroupMemberRoleLevels(ctx context.Context, groupID string, roleLevels []int32) (groupMembers []*model.GroupMember, err error)
|
||||
// FindGroupMemberAll retrieves all members of a group.
|
||||
FindGroupMemberAll(ctx context.Context, groupID string) (groupMembers []*model.GroupMember, err error)
|
||||
// FindGroupsOwner retrieves the owners for multiple groups.
|
||||
FindGroupsOwner(ctx context.Context, groupIDs []string) ([]*model.GroupMember, error)
|
||||
// FindGroupMemberUserID retrieves the user IDs of all members in a group.
|
||||
FindGroupMemberUserID(ctx context.Context, groupID string) ([]string, error)
|
||||
// FindGroupMemberNum retrieves the number of members in a group.
|
||||
FindGroupMemberNum(ctx context.Context, groupID string) (uint32, error)
|
||||
// FindUserManagedGroupID retrieves group IDs managed by a user.
|
||||
FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error)
|
||||
// PageGroupRequest paginates through group requests for specified groups.
|
||||
PageGroupRequest(ctx context.Context, groupIDs []string, pagination pagination.Pagination) (int64, []*model.GroupRequest, error)
|
||||
// GetGroupRoleLevelMemberIDs retrieves user IDs of group members with a specific role level.
|
||||
GetGroupRoleLevelMemberIDs(ctx context.Context, groupID string, roleLevel int32) ([]string, error)
|
||||
|
||||
// PageGetJoinGroup paginates through groups that a user has joined.
|
||||
PageGetJoinGroup(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, totalGroupMembers []*model.GroupMember, err error)
|
||||
// PageGetGroupMember paginates through members of a group.
|
||||
PageGetGroupMember(ctx context.Context, groupID string, pagination pagination.Pagination) (total int64, totalGroupMembers []*model.GroupMember, err error)
|
||||
// SearchGroupMember searches for group members based on a keyword, group ID, and pagination settings.
|
||||
SearchGroupMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (int64, []*model.GroupMember, error)
|
||||
// HandlerGroupRequest processes a group join request with a specified result.
|
||||
HandlerGroupRequest(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32, member *model.GroupMember) error
|
||||
// DeleteGroupMember removes specified users from a group.
|
||||
DeleteGroupMember(ctx context.Context, groupID string, userIDs []string) error
|
||||
// MapGroupMemberUserID maps group IDs to their members' simplified user IDs.
|
||||
MapGroupMemberUserID(ctx context.Context, groupIDs []string) (map[string]*common.GroupSimpleUserID, error)
|
||||
// MapGroupMemberNum maps group IDs to their member count.
|
||||
MapGroupMemberNum(ctx context.Context, groupIDs []string) (map[string]uint32, error)
|
||||
// TransferGroupOwner transfers the ownership of a group to another user.
|
||||
TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string, roleLevel int32) error
|
||||
// UpdateGroupMember updates properties of a group member.
|
||||
UpdateGroupMember(ctx context.Context, groupID string, userID string, data map[string]any) error
|
||||
// UpdateGroupMembers batch updates properties of group members.
|
||||
UpdateGroupMembers(ctx context.Context, data []*common.BatchUpdateGroupMember) error
|
||||
|
||||
// CreateGroupRequest creates new group join requests.
|
||||
CreateGroupRequest(ctx context.Context, requests []*model.GroupRequest) error
|
||||
// TakeGroupRequest retrieves a specific group join request.
|
||||
TakeGroupRequest(ctx context.Context, groupID string, userID string) (*model.GroupRequest, error)
|
||||
// FindGroupRequests retrieves multiple group join requests.
|
||||
FindGroupRequests(ctx context.Context, groupID string, userIDs []string) ([]*model.GroupRequest, error)
|
||||
// PageGroupRequestUser paginates through group join requests made by a user.
|
||||
PageGroupRequestUser(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*model.GroupRequest, error)
|
||||
|
||||
// CountTotal counts the total number of groups as of a certain date.
|
||||
CountTotal(ctx context.Context, before *time.Time) (count int64, err error)
|
||||
// CountRangeEverydayTotal counts the daily group creation total within a specified date range.
|
||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||
// DeleteGroupMemberHash deletes the hash entries for group members in specified groups.
|
||||
DeleteGroupMemberHash(ctx context.Context, groupIDs []string) error
|
||||
}
|
||||
|
||||
func NewGroupDatabase(
|
||||
rdb redis.UniversalClient,
|
||||
localCache *config.LocalCache,
|
||||
groupDB database.Group,
|
||||
groupMemberDB database.GroupMember,
|
||||
groupRequestDB database.GroupRequest,
|
||||
ctxTx tx.Tx,
|
||||
groupHash cache.GroupHash,
|
||||
) GroupDatabase {
|
||||
return &groupDatabase{
|
||||
groupDB: groupDB,
|
||||
groupMemberDB: groupMemberDB,
|
||||
groupRequestDB: groupRequestDB,
|
||||
ctxTx: ctxTx,
|
||||
cache: redis2.NewGroupCacheRedis(rdb, localCache, groupDB, groupMemberDB, groupRequestDB, groupHash, redis2.GetRocksCacheOptions()),
|
||||
}
|
||||
}
|
||||
|
||||
type groupDatabase struct {
|
||||
groupDB database.Group
|
||||
groupMemberDB database.GroupMember
|
||||
groupRequestDB database.GroupRequest
|
||||
ctxTx tx.Tx
|
||||
cache cache.GroupCache
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*model.GroupMember, error) {
|
||||
return g.cache.GetGroupMembersInfo(ctx, groupID, userIDs)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupMemberUser(ctx context.Context, groupIDs []string, userID string) ([]*model.GroupMember, error) {
|
||||
return g.cache.FindGroupMemberUser(ctx, groupIDs, userID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupMemberRoleLevels(ctx context.Context, groupID string, roleLevels []int32) ([]*model.GroupMember, error) {
|
||||
return g.cache.GetGroupRolesLevelMemberInfo(ctx, groupID, roleLevels)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupMemberAll(ctx context.Context, groupID string) ([]*model.GroupMember, error) {
|
||||
return g.cache.GetAllGroupMembersInfo(ctx, groupID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupsOwner(ctx context.Context, groupIDs []string) ([]*model.GroupMember, error) {
|
||||
return g.cache.GetGroupsOwner(ctx, groupIDs)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) GetGroupRoleLevelMemberIDs(ctx context.Context, groupID string, roleLevel int32) ([]string, error) {
|
||||
return g.cache.GetGroupRoleLevelMemberIDs(ctx, groupID, roleLevel)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) CreateGroup(ctx context.Context, groups []*model.Group, groupMembers []*model.GroupMember) error {
|
||||
if len(groups)+len(groupMembers) == 0 {
|
||||
return nil
|
||||
}
|
||||
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
|
||||
c := g.cache.CloneGroupCache()
|
||||
if len(groups) > 0 {
|
||||
if err := g.groupDB.Create(ctx, groups); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, group := range groups {
|
||||
c = c.DelGroupsInfo(group.GroupID).
|
||||
DelGroupMembersHash(group.GroupID).
|
||||
DelGroupMembersHash(group.GroupID).
|
||||
DelGroupsMemberNum(group.GroupID).
|
||||
DelGroupMemberIDs(group.GroupID).
|
||||
DelGroupAllRoleLevel(group.GroupID)
|
||||
}
|
||||
}
|
||||
if len(groupMembers) > 0 {
|
||||
if err := g.groupMemberDB.Create(ctx, groupMembers); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, groupMember := range groupMembers {
|
||||
c = c.DelGroupMembersHash(groupMember.GroupID).
|
||||
DelGroupsMemberNum(groupMember.GroupID).
|
||||
DelGroupMemberIDs(groupMember.GroupID).
|
||||
DelJoinedGroupID(groupMember.UserID).
|
||||
DelGroupMembersInfo(groupMember.GroupID, groupMember.UserID).
|
||||
DelGroupAllRoleLevel(groupMember.GroupID)
|
||||
}
|
||||
}
|
||||
return c.ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupMemberUserID(ctx context.Context, groupID string) ([]string, error) {
|
||||
return g.cache.GetGroupMemberIDs(ctx, groupID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupMemberNum(ctx context.Context, groupID string) (uint32, error) {
|
||||
num, err := g.cache.GetGroupMemberNum(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(num), nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) TakeGroup(ctx context.Context, groupID string) (*model.Group, error) {
|
||||
return g.cache.GetGroupInfo(ctx, groupID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroup(ctx context.Context, groupIDs []string) ([]*model.Group, error) {
|
||||
return g.cache.GetGroupsInfo(ctx, groupIDs)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) SearchGroup(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*model.Group, error) {
|
||||
return g.groupDB.Search(ctx, keyword, pagination)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) UpdateGroup(ctx context.Context, groupID string, data map[string]any) error {
|
||||
if err := g.groupDB.UpdateMap(ctx, groupID, data); err != nil {
|
||||
return err
|
||||
}
|
||||
return g.cache.DelGroupsInfo(groupID).ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) DismissGroup(ctx context.Context, groupID string, deleteMember bool) error {
|
||||
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
|
||||
c := g.cache.CloneGroupCache()
|
||||
if err := g.groupDB.UpdateStatus(ctx, groupID, constant.GroupStatusDismissed); err != nil {
|
||||
return err
|
||||
}
|
||||
if deleteMember {
|
||||
userIDs, err := g.cache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.groupMemberDB.Delete(ctx, groupID, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
c = c.DelJoinedGroupID(userIDs...).
|
||||
DelGroupMemberIDs(groupID).
|
||||
DelGroupsMemberNum(groupID).
|
||||
DelGroupMembersHash(groupID).
|
||||
DelGroupAllRoleLevel(groupID).
|
||||
DelGroupMembersInfo(groupID, userIDs...)
|
||||
}
|
||||
return c.DelGroupsInfo(groupID).ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *groupDatabase) TakeGroupMember(ctx context.Context, groupID string, userID string) (*model.GroupMember, error) {
|
||||
return g.cache.GetGroupMemberInfo(ctx, groupID, userID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) TakeGroupOwner(ctx context.Context, groupID string) (*model.GroupMember, error) {
|
||||
return g.cache.GetGroupOwner(ctx, groupID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) {
|
||||
return g.groupMemberDB.FindUserManagedGroupID(ctx, userID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) PageGroupRequest(ctx context.Context, groupIDs []string, pagination pagination.Pagination) (int64, []*model.GroupRequest, error) {
|
||||
return g.groupRequestDB.PageGroup(ctx, groupIDs, pagination)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) PageGetJoinGroup(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, totalGroupMembers []*model.GroupMember, err error) {
|
||||
groupIDs, err := g.cache.GetJoinedGroupIDs(ctx, userID)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, groupID := range datautil.Paginate(groupIDs, int(pagination.GetPageNumber()), int(pagination.GetShowNumber())) {
|
||||
groupMembers, err := g.cache.GetGroupMembersInfo(ctx, groupID, []string{userID})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
totalGroupMembers = append(totalGroupMembers, groupMembers...)
|
||||
}
|
||||
return int64(len(groupIDs)), totalGroupMembers, nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) PageGetGroupMember(ctx context.Context, groupID string, pagination pagination.Pagination) (total int64, totalGroupMembers []*model.GroupMember, err error) {
|
||||
groupMemberIDs, err := g.cache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
pageIDs := datautil.Paginate(groupMemberIDs, int(pagination.GetPageNumber()), int(pagination.GetShowNumber()))
|
||||
if len(pageIDs) == 0 {
|
||||
return int64(len(groupMemberIDs)), nil, nil
|
||||
}
|
||||
members, err := g.cache.GetGroupMembersInfo(ctx, groupID, pageIDs)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return int64(len(groupMemberIDs)), members, nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) SearchGroupMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (int64, []*model.GroupMember, error) {
|
||||
return g.groupMemberDB.SearchMember(ctx, keyword, groupID, pagination)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) HandlerGroupRequest(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32, member *model.GroupMember) error {
|
||||
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err := g.groupRequestDB.UpdateHandler(ctx, groupID, userID, handledMsg, handleResult); err != nil {
|
||||
return err
|
||||
}
|
||||
if member != nil {
|
||||
c := g.cache.CloneGroupCache()
|
||||
if err := g.groupMemberDB.Create(ctx, []*model.GroupMember{member}); err != nil {
|
||||
return err
|
||||
}
|
||||
c = c.DelGroupMembersHash(groupID).
|
||||
DelGroupMembersInfo(groupID, member.UserID).
|
||||
DelGroupMemberIDs(groupID).
|
||||
DelGroupsMemberNum(groupID).
|
||||
DelJoinedGroupID(member.UserID).
|
||||
DelGroupRoleLevel(groupID, []int32{member.RoleLevel})
|
||||
if err := c.ChainExecDel(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (g *groupDatabase) DeleteGroupMember(ctx context.Context, groupID string, userIDs []string) error {
|
||||
if err := g.groupMemberDB.Delete(ctx, groupID, userIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
c := g.cache.CloneGroupCache()
|
||||
return c.DelGroupMembersHash(groupID).
|
||||
DelGroupMemberIDs(groupID).
|
||||
DelGroupsMemberNum(groupID).
|
||||
DelJoinedGroupID(userIDs...).
|
||||
DelGroupMembersInfo(groupID, userIDs...).
|
||||
DelGroupAllRoleLevel(groupID).
|
||||
ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) MapGroupMemberUserID(ctx context.Context, groupIDs []string) (map[string]*common.GroupSimpleUserID, error) {
|
||||
return g.cache.GetGroupMemberHashMap(ctx, groupIDs)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) MapGroupMemberNum(ctx context.Context, groupIDs []string) (m map[string]uint32, err error) {
|
||||
m = make(map[string]uint32)
|
||||
for _, groupID := range groupIDs {
|
||||
num, err := g.cache.GetGroupMemberNum(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[groupID] = uint32(num)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string, roleLevel int32) error {
|
||||
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err := g.groupMemberDB.UpdateRoleLevel(ctx, groupID, oldOwnerUserID, roleLevel); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.groupMemberDB.UpdateRoleLevel(ctx, groupID, newOwnerUserID, constant.GroupOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
c := g.cache.CloneGroupCache()
|
||||
return c.DelGroupMembersInfo(groupID, oldOwnerUserID, newOwnerUserID).
|
||||
DelGroupAllRoleLevel(groupID).
|
||||
DelGroupMembersHash(groupID).ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *groupDatabase) UpdateGroupMember(ctx context.Context, groupID string, userID string, data map[string]any) error {
|
||||
if err := g.groupMemberDB.Update(ctx, groupID, userID, data); err != nil {
|
||||
return err
|
||||
}
|
||||
c := g.cache.CloneGroupCache()
|
||||
c = c.DelGroupMembersInfo(groupID, userID)
|
||||
if g.groupMemberDB.IsUpdateRoleLevel(data) {
|
||||
c = c.DelGroupAllRoleLevel(groupID)
|
||||
}
|
||||
return c.ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) UpdateGroupMembers(ctx context.Context, data []*common.BatchUpdateGroupMember) error {
|
||||
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
|
||||
c := g.cache.CloneGroupCache()
|
||||
for _, item := range data {
|
||||
if err := g.groupMemberDB.Update(ctx, item.GroupID, item.UserID, item.Map); err != nil {
|
||||
return err
|
||||
}
|
||||
if g.groupMemberDB.IsUpdateRoleLevel(item.Map) {
|
||||
c = c.DelGroupAllRoleLevel(item.GroupID)
|
||||
}
|
||||
c = c.DelGroupMembersInfo(item.GroupID, item.UserID).DelGroupMembersHash(item.GroupID)
|
||||
}
|
||||
return c.ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *groupDatabase) CreateGroupRequest(ctx context.Context, requests []*model.GroupRequest) error {
|
||||
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
|
||||
for _, request := range requests {
|
||||
if err := g.groupRequestDB.Delete(ctx, request.GroupID, request.UserID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return g.groupRequestDB.Create(ctx, requests)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *groupDatabase) TakeGroupRequest(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
) (*model.GroupRequest, error) {
|
||||
return g.groupRequestDB.Take(ctx, groupID, userID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) PageGroupRequestUser(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*model.GroupRequest, error) {
|
||||
return g.groupRequestDB.Page(ctx, userID, pagination)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
||||
return g.groupDB.CountTotal(ctx, before)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
|
||||
return g.groupDB.CountRangeEverydayTotal(ctx, start, end)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupRequests(ctx context.Context, groupID string, userIDs []string) ([]*model.GroupRequest, error) {
|
||||
return g.groupRequestDB.FindGroupRequests(ctx, groupID, userIDs)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) DeleteGroupMemberHash(ctx context.Context, groupIDs []string) error {
|
||||
if len(groupIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
c := g.cache.CloneGroupCache()
|
||||
for _, groupID := range groupIDs {
|
||||
c = c.DelGroupMembersHash(groupID)
|
||||
}
|
||||
return c.ChainExecDel(ctx)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
)
|
||||
|
||||
type PushDatabase interface {
|
||||
DelFcmToken(ctx context.Context, userID string, platformID int) error
|
||||
}
|
||||
|
||||
type pushDataBase struct {
|
||||
cache cache.ThirdCache
|
||||
}
|
||||
|
||||
func NewPushDatabase(cache cache.ThirdCache) PushDatabase {
|
||||
return &pushDataBase{cache: cache}
|
||||
}
|
||||
|
||||
func (p *pushDataBase) DelFcmToken(ctx context.Context, userID string, platformID int) error {
|
||||
return p.cache.DelFcmToken(ctx, userID, platformID)
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
redis2 "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/tools/s3"
|
||||
"github.com/openimsdk/tools/s3/cont"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type S3Database interface {
|
||||
PartLimit() *s3.PartLimit
|
||||
PartSize(ctx context.Context, size int64) (int64, error)
|
||||
AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error)
|
||||
InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error)
|
||||
CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error)
|
||||
AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error)
|
||||
SetObject(ctx context.Context, info *model.Object) error
|
||||
StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error)
|
||||
FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error)
|
||||
}
|
||||
|
||||
func NewS3Database(rdb redis.UniversalClient, s3 s3.Interface, obj database.ObjectInfo) S3Database {
|
||||
return &s3Database{
|
||||
s3: cont.New(redis2.NewS3Cache(rdb, s3), s3),
|
||||
cache: redis2.NewObjectCacheRedis(rdb, obj),
|
||||
db: obj,
|
||||
}
|
||||
}
|
||||
|
||||
type s3Database struct {
|
||||
s3 *cont.Controller
|
||||
cache cache.ObjectCache
|
||||
db database.ObjectInfo
|
||||
}
|
||||
|
||||
func (s *s3Database) PartSize(ctx context.Context, size int64) (int64, error) {
|
||||
return s.s3.PartSize(ctx, size)
|
||||
}
|
||||
|
||||
func (s *s3Database) PartLimit() *s3.PartLimit {
|
||||
return s.s3.PartLimit()
|
||||
}
|
||||
|
||||
func (s *s3Database) AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) {
|
||||
return s.s3.AuthSign(ctx, uploadID, partNumbers)
|
||||
}
|
||||
|
||||
func (s *s3Database) InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error) {
|
||||
return s.s3.InitiateUpload(ctx, hash, size, expire, maxParts)
|
||||
}
|
||||
|
||||
func (s *s3Database) CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error) {
|
||||
return s.s3.CompleteUpload(ctx, uploadID, parts)
|
||||
}
|
||||
|
||||
func (s *s3Database) SetObject(ctx context.Context, info *model.Object) error {
|
||||
info.Engine = s.s3.Engine()
|
||||
if err := s.db.SetObject(ctx, info); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.cache.DelObjectName(info.Engine, info.Name).ChainExecDel(ctx)
|
||||
}
|
||||
|
||||
func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error) {
|
||||
obj, err := s.cache.GetName(ctx, s.s3.Engine(), name)
|
||||
if err != nil {
|
||||
return time.Time{}, "", err
|
||||
}
|
||||
if opt == nil {
|
||||
opt = &s3.AccessURLOption{}
|
||||
}
|
||||
if opt.ContentType == "" {
|
||||
opt.ContentType = obj.ContentType
|
||||
}
|
||||
if opt.Filename == "" {
|
||||
opt.Filename = filepath.Base(obj.Name)
|
||||
}
|
||||
expireTime := time.Now().Add(expire)
|
||||
rawURL, err := s.s3.AccessURL(ctx, obj.Key, expire, opt)
|
||||
if err != nil {
|
||||
return time.Time{}, "", err
|
||||
}
|
||||
return expireTime, rawURL, nil
|
||||
}
|
||||
|
||||
func (s *s3Database) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
|
||||
return s.s3.StatObject(ctx, name)
|
||||
}
|
||||
|
||||
func (s *s3Database) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
|
||||
return s.s3.FormData(ctx, name, size, contentType, duration)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
type ThirdDatabase interface {
|
||||
FcmUpdateToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) error
|
||||
SetAppBadge(ctx context.Context, userID string, value int) error
|
||||
// about log for debug
|
||||
UploadLogs(ctx context.Context, logs []*model.Log) error
|
||||
DeleteLogs(ctx context.Context, logID []string, userID string) error
|
||||
SearchLogs(ctx context.Context, keyword string, start time.Time, end time.Time, pagination pagination.Pagination) (int64, []*model.Log, error)
|
||||
GetLogs(ctx context.Context, LogIDs []string, userID string) ([]*model.Log, error)
|
||||
}
|
||||
|
||||
type thirdDatabase struct {
|
||||
cache cache.ThirdCache
|
||||
logdb database.Log
|
||||
}
|
||||
|
||||
// DeleteLogs implements ThirdDatabase.
|
||||
func (t *thirdDatabase) DeleteLogs(ctx context.Context, logID []string, userID string) error {
|
||||
return t.logdb.Delete(ctx, logID, userID)
|
||||
}
|
||||
|
||||
// GetLogs implements ThirdDatabase.
|
||||
func (t *thirdDatabase) GetLogs(ctx context.Context, LogIDs []string, userID string) ([]*model.Log, error) {
|
||||
return t.logdb.Get(ctx, LogIDs, userID)
|
||||
}
|
||||
|
||||
// SearchLogs implements ThirdDatabase.
|
||||
func (t *thirdDatabase) SearchLogs(ctx context.Context, keyword string, start time.Time, end time.Time, pagination pagination.Pagination) (int64, []*model.Log, error) {
|
||||
return t.logdb.Search(ctx, keyword, start, end, pagination)
|
||||
}
|
||||
|
||||
// UploadLogs implements ThirdDatabase.
|
||||
func (t *thirdDatabase) UploadLogs(ctx context.Context, logs []*model.Log) error {
|
||||
return t.logdb.Create(ctx, logs)
|
||||
}
|
||||
|
||||
func NewThirdDatabase(cache cache.ThirdCache, logdb database.Log) ThirdDatabase {
|
||||
return &thirdDatabase{cache: cache, logdb: logdb}
|
||||
}
|
||||
|
||||
func (t *thirdDatabase) FcmUpdateToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) error {
|
||||
return t.cache.SetFcmToken(ctx, account, platformID, fcmToken, expireTime)
|
||||
}
|
||||
|
||||
func (t *thirdDatabase) SetAppBadge(ctx context.Context, userID string, value int) error {
|
||||
return t.cache.SetUserBadgeUnreadCountSum(ctx, userID, value)
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/db/tx"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/protocol/user"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
)
|
||||
|
||||
type UserDatabase interface {
|
||||
// FindWithError Get the information of the specified user. If the userID is not found, it will also return an error
|
||||
FindWithError(ctx context.Context, userIDs []string) (users []*model.User, err error)
|
||||
// Find Get the information of the specified user If the userID is not found, no error will be returned
|
||||
Find(ctx context.Context, userIDs []string) (users []*model.User, err error)
|
||||
// Find userInfo By Nickname
|
||||
FindByNickname(ctx context.Context, nickname string) (users []*model.User, err error)
|
||||
// Find notificationAccounts
|
||||
FindNotification(ctx context.Context, level int64) (users []*model.User, err error)
|
||||
// Create Insert multiple external guarantees that the userID is not repeated and does not exist in the storage
|
||||
Create(ctx context.Context, users []*model.User) (err error)
|
||||
// UpdateByMap update (zero value) external guarantee userID exists
|
||||
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
|
||||
// FindUser
|
||||
PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*model.User, err error)
|
||||
// FindUser with keyword
|
||||
PageFindUserWithKeyword(ctx context.Context, level1 int64, level2 int64, userID string, nickName string, pagination pagination.Pagination) (count int64, users []*model.User, err error)
|
||||
// Page If not found, no error is returned
|
||||
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*model.User, err error)
|
||||
// IsExist true as long as one exists
|
||||
IsExist(ctx context.Context, userIDs []string) (exist bool, err error)
|
||||
// GetAllUserID Get all user IDs
|
||||
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error)
|
||||
// Get user by userID
|
||||
GetUserByID(ctx context.Context, userID string) (user *model.User, err error)
|
||||
// InitOnce Inside the function, first query whether it exists in the storage, if it exists, do nothing; if it does not exist, insert it
|
||||
InitOnce(ctx context.Context, users []*model.User) (err error)
|
||||
// CountTotal Get the total number of users
|
||||
CountTotal(ctx context.Context, before *time.Time) (int64, error)
|
||||
// CountRangeEverydayTotal Get the user increment in the range
|
||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||
// SubscribeUsersStatus Subscribe a user's presence status
|
||||
SubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error
|
||||
// UnsubscribeUsersStatus unsubscribe a user's presence status
|
||||
UnsubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error
|
||||
// GetAllSubscribeList Get a list of all subscriptions
|
||||
GetAllSubscribeList(ctx context.Context, userID string) ([]string, error)
|
||||
// GetSubscribedList Get all subscribed lists
|
||||
GetSubscribedList(ctx context.Context, userID string) ([]string, error)
|
||||
// GetUserStatus Get the online status of the user
|
||||
GetUserStatus(ctx context.Context, userIDs []string) ([]*user.OnlineStatus, error)
|
||||
// SetUserStatus Set the user status and store the user status in redis
|
||||
SetUserStatus(ctx context.Context, userID string, status, platformID int32) error
|
||||
|
||||
// CRUD user command
|
||||
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error
|
||||
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
|
||||
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error
|
||||
GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error)
|
||||
GetAllUserCommands(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error)
|
||||
}
|
||||
|
||||
type userDatabase struct {
|
||||
tx tx.Tx
|
||||
userDB database.User
|
||||
cache cache.UserCache
|
||||
mongoDB database.SubscribeUser
|
||||
}
|
||||
|
||||
func NewUserDatabase(userDB database.User, cache cache.UserCache, tx tx.Tx, mongoDB database.SubscribeUser) UserDatabase {
|
||||
return &userDatabase{userDB: userDB, cache: cache, tx: tx, mongoDB: mongoDB}
|
||||
}
|
||||
|
||||
func (u *userDatabase) InitOnce(ctx context.Context, users []*model.User) error {
|
||||
// Extract user IDs from the given user models.
|
||||
userIDs := datautil.Slice(users, func(e *model.User) string {
|
||||
return e.UserID
|
||||
})
|
||||
|
||||
// Find existing users in the database.
|
||||
existingUsers, err := u.userDB.Find(ctx, userIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Determine which users are missing from the database.
|
||||
missingUsers := datautil.SliceAnySub(users, existingUsers, func(e *model.User) string {
|
||||
return e.UserID
|
||||
})
|
||||
|
||||
// Create records for missing users.
|
||||
if len(missingUsers) > 0 {
|
||||
if err := u.userDB.Create(ctx, missingUsers); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindWithError Get the information of the specified user and return an error if the userID is not found.
|
||||
func (u *userDatabase) FindWithError(ctx context.Context, userIDs []string) (users []*model.User, err error) {
|
||||
users, err = u.cache.GetUsersInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(users) != len(userIDs) {
|
||||
err = errs.ErrRecordNotFound.WrapMsg("userID not found")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Find Get the information of the specified user. If the userID is not found, no error will be returned.
|
||||
func (u *userDatabase) Find(ctx context.Context, userIDs []string) (users []*model.User, err error) {
|
||||
return u.cache.GetUsersInfo(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (u *userDatabase) FindByNickname(ctx context.Context, nickname string) (users []*model.User, err error) {
|
||||
return u.userDB.TakeByNickname(ctx, nickname)
|
||||
}
|
||||
|
||||
func (u *userDatabase) FindNotification(ctx context.Context, level int64) (users []*model.User, err error) {
|
||||
return u.userDB.TakeNotification(ctx, level)
|
||||
}
|
||||
|
||||
// Create Insert multiple external guarantees that the userID is not repeated and does not exist in the storage.
|
||||
func (u *userDatabase) Create(ctx context.Context, users []*model.User) (err error) {
|
||||
return u.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err = u.userDB.Create(ctx, users); err != nil {
|
||||
return err
|
||||
}
|
||||
return u.cache.DelUsersInfo(datautil.Slice(users, func(e *model.User) string {
|
||||
return e.UserID
|
||||
})...).ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateByMap update (zero value) externally guarantees that userID exists.
|
||||
func (u *userDatabase) UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) {
|
||||
return u.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err := u.userDB.UpdateByMap(ctx, userID, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return u.cache.DelUsersInfo(userID).ChainExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// Page Gets, returns no error if not found.
|
||||
func (u *userDatabase) Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*model.User, err error) {
|
||||
return u.userDB.Page(ctx, pagination)
|
||||
}
|
||||
|
||||
func (u *userDatabase) PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*model.User, err error) {
|
||||
return u.userDB.PageFindUser(ctx, level1, level2, pagination)
|
||||
}
|
||||
|
||||
func (u *userDatabase) PageFindUserWithKeyword(ctx context.Context, level1 int64, level2 int64, userID, nickName string, pagination pagination.Pagination) (count int64, users []*model.User, err error) {
|
||||
return u.userDB.PageFindUserWithKeyword(ctx, level1, level2, userID, nickName, pagination)
|
||||
}
|
||||
|
||||
// IsExist Does userIDs exist? As long as there is one, it will be true.
|
||||
func (u *userDatabase) IsExist(ctx context.Context, userIDs []string) (exist bool, err error) {
|
||||
users, err := u.userDB.Find(ctx, userIDs)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(users) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// GetAllUserID Get all user IDs.
|
||||
func (u *userDatabase) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (total int64, userIDs []string, err error) {
|
||||
return u.userDB.GetAllUserID(ctx, pagination)
|
||||
}
|
||||
|
||||
func (u *userDatabase) GetUserByID(ctx context.Context, userID string) (user *model.User, err error) {
|
||||
return u.userDB.Take(ctx, userID)
|
||||
}
|
||||
|
||||
// CountTotal Get the total number of users.
|
||||
func (u *userDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
||||
return u.userDB.CountTotal(ctx, before)
|
||||
}
|
||||
|
||||
// CountRangeEverydayTotal Get the user increment in the range.
|
||||
func (u *userDatabase) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
|
||||
return u.userDB.CountRangeEverydayTotal(ctx, start, end)
|
||||
}
|
||||
|
||||
// SubscribeUsersStatus Subscribe or unsubscribe a user's presence status.
|
||||
func (u *userDatabase) SubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error {
|
||||
err := u.mongoDB.AddSubscriptionList(ctx, userID, userIDs)
|
||||
return err
|
||||
}
|
||||
|
||||
// UnsubscribeUsersStatus unsubscribe a user's presence status.
|
||||
func (u *userDatabase) UnsubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error {
|
||||
err := u.mongoDB.UnsubscriptionList(ctx, userID, userIDs)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAllSubscribeList Get a list of all subscriptions.
|
||||
func (u *userDatabase) GetAllSubscribeList(ctx context.Context, userID string) ([]string, error) {
|
||||
list, err := u.mongoDB.GetAllSubscribeList(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// GetSubscribedList Get all subscribed lists.
|
||||
func (u *userDatabase) GetSubscribedList(ctx context.Context, userID string) ([]string, error) {
|
||||
list, err := u.mongoDB.GetSubscribedList(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// GetUserStatus get user status.
|
||||
func (u *userDatabase) GetUserStatus(ctx context.Context, userIDs []string) ([]*user.OnlineStatus, error) {
|
||||
onlineStatusList, err := u.cache.GetUserStatus(ctx, userIDs)
|
||||
return onlineStatusList, err
|
||||
}
|
||||
|
||||
// SetUserStatus Set the user status and save it in redis.
|
||||
func (u *userDatabase) SetUserStatus(ctx context.Context, userID string, status, platformID int32) error {
|
||||
return u.cache.SetUserStatus(ctx, userID, status, platformID)
|
||||
}
|
||||
|
||||
func (u *userDatabase) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error {
|
||||
return u.userDB.AddUserCommand(ctx, userID, Type, UUID, value, ex)
|
||||
}
|
||||
|
||||
func (u *userDatabase) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error {
|
||||
return u.userDB.DeleteUserCommand(ctx, userID, Type, UUID)
|
||||
}
|
||||
|
||||
func (u *userDatabase) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error {
|
||||
return u.userDB.UpdateUserCommand(ctx, userID, Type, UUID, val)
|
||||
}
|
||||
|
||||
func (u *userDatabase) GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) {
|
||||
commands, err := u.userDB.GetUserCommand(ctx, userID, Type)
|
||||
return commands, err
|
||||
}
|
||||
|
||||
func (u *userDatabase) GetAllUserCommands(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error) {
|
||||
commands, err := u.userDB.GetAllUserCommand(ctx, userID)
|
||||
return commands, err
|
||||
}
|
||||
Reference in New Issue
Block a user