From a3bda263a7ef91b1a9c36a6d5b340ce0f70ca3fb Mon Sep 17 00:00:00 2001 From: hawklin2017 <32898629+hawklin2017@users.noreply.github.com> Date: Sat, 11 Apr 2026 21:26:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A5=BD=E5=8F=8B=E7=BD=AE=E9=A1=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/api/friend.go | 4 +++ internal/api/router.go | 1 + internal/rpc/relation/friend.go | 36 +++++++++++++++++++++++ pkg/common/storage/controller/friend.go | 6 ++++ pkg/common/storage/database/friend.go | 2 ++ pkg/common/storage/database/mgo/friend.go | 18 ++++++++++++ 6 files changed, 67 insertions(+) diff --git a/internal/api/friend.go b/internal/api/friend.go index 0943e8a5d..023ae28c8 100644 --- a/internal/api/friend.go +++ b/internal/api/friend.go @@ -118,3 +118,7 @@ func (o *FriendApi) GetFullFriendUserIDs(c *gin.Context) { func (o *FriendApi) GetSelfUnhandledApplyCount(c *gin.Context) { a2r.Call(c, relation.FriendClient.GetSelfUnhandledApplyCount, o.Client) } + +func (o *FriendApi) GetPinnedFriendIDs(c *gin.Context) { + a2r.Call(c, relation.FriendClient.GetPinnedFriendIDs, o.Client) +} diff --git a/internal/api/router.go b/internal/api/router.go index 936b60531..46437a020 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -190,6 +190,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co friendRouterGroup.POST("/get_incremental_friends", f.GetIncrementalFriends) friendRouterGroup.POST("/get_full_friend_user_ids", f.GetFullFriendUserIDs) friendRouterGroup.POST("/get_self_unhandled_apply_count", f.GetSelfUnhandledApplyCount) + friendRouterGroup.POST("/get_pinned_friend_ids", f.GetPinnedFriendIDs) } g := NewGroupApi(group.NewGroupClient(groupConn)) diff --git a/internal/rpc/relation/friend.go b/internal/rpc/relation/friend.go index 77f42e1a6..b32a8c50d 100644 --- a/internal/rpc/relation/friend.go +++ b/internal/rpc/relation/friend.go @@ -34,12 +34,15 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" + "github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil" "github.com/openimsdk/protocol/constant" + conversationpb "github.com/openimsdk/protocol/conversation" "github.com/openimsdk/protocol/relation" "github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/tools/db/mongoutil" "github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/errs" + "github.com/openimsdk/tools/log" "github.com/openimsdk/tools/utils/datautil" "google.golang.org/grpc" ) @@ -54,6 +57,7 @@ type friendServer struct { webhookClient *webhook.Client queue *memamq.MemoryQueue userClient *rpcli.UserClient + conversationClient *rpcli.ConversationClient } type Config struct { @@ -101,6 +105,10 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg if err != nil { return err } + conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation) + if err != nil { + return err + } userClient := rpcli.NewUserClient(userConn) database := controller.NewFriendDatabase( @@ -131,6 +139,7 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL), queue: memamq.NewMemoryQueue(16, 1024*1024), userClient: userClient, + conversationClient: rpcli.NewConversationClient(conversationConn), }) return nil } @@ -549,6 +558,22 @@ func (s *friendServer) UpdateFriends( return nil, err } + if req.IsPinned != nil { + for _, friendUserID := range req.FriendUserIDs { + convID := conversationutil.GenConversationIDForSingle(req.OwnerUserID, friendUserID) + if err := s.conversationClient.SetConversations(ctx, []string{req.OwnerUserID}, + &conversationpb.ConversationReq{ + ConversationID: convID, + ConversationType: constant.SingleChatType, + UserID: friendUserID, + IsPinned: req.IsPinned, + }); err != nil { + log.ZWarn(ctx, "sync conversation isPinned failed", err, + "ownerUserID", req.OwnerUserID, "friendUserID", friendUserID) + } + } + } + resp := &relation.UpdateFriendsResp{} s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs) @@ -570,6 +595,17 @@ func (s *friendServer) GetSelfUnhandledApplyCount(ctx context.Context, req *rela }, nil } +func (s *friendServer) GetPinnedFriendIDs(ctx context.Context, req *relation.GetPinnedFriendIDsReq) (*relation.GetPinnedFriendIDsResp, error) { + if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil { + return nil, err + } + ids, err := s.db.GetPinnedFriendIDs(ctx, req.UserID) + if err != nil { + return nil, err + } + return &relation.GetPinnedFriendIDsResp{FriendUserIDs: ids}, nil +} + func (s *friendServer) getCommonUserMap(ctx context.Context, userIDs []string) (map[string]common_user.CommonUser, error) { users, err := s.userClient.GetUsersInfo(ctx, userIDs) if err != nil { diff --git a/pkg/common/storage/controller/friend.go b/pkg/common/storage/controller/friend.go index b2ae3e732..db0b34216 100644 --- a/pkg/common/storage/controller/friend.go +++ b/pkg/common/storage/controller/friend.go @@ -90,6 +90,8 @@ type FriendDatabase interface { OwnerIncrVersion(ctx context.Context, ownerUserID string, friendUserIDs []string, state int32) error GetUnhandledCount(ctx context.Context, userID string, ts int64) (int64, error) + + GetPinnedFriendIDs(ctx context.Context, ownerUserID string) ([]string, error) } type friendDatabase struct { @@ -402,3 +404,7 @@ func (f *friendDatabase) OwnerIncrVersion(ctx context.Context, ownerUserID strin func (f *friendDatabase) GetUnhandledCount(ctx context.Context, userID string, ts int64) (int64, error) { return f.friendRequest.GetUnhandledCount(ctx, userID, ts) } + +func (f *friendDatabase) GetPinnedFriendIDs(ctx context.Context, ownerUserID string) ([]string, error) { + return f.friend.FindPinnedFriendUserIDs(ctx, ownerUserID) +} diff --git a/pkg/common/storage/database/friend.go b/pkg/common/storage/database/friend.go index b596411fc..d89b18cb2 100644 --- a/pkg/common/storage/database/friend.go +++ b/pkg/common/storage/database/friend.go @@ -57,4 +57,6 @@ type Friend interface { FindOwnerFriendUserIds(ctx context.Context, ownerUserID string, limit int) ([]string, error) IncrVersion(ctx context.Context, ownerUserID string, friendUserIDs []string, state int32) error + + FindPinnedFriendUserIDs(ctx context.Context, ownerUserID string) ([]string, error) } diff --git a/pkg/common/storage/database/mgo/friend.go b/pkg/common/storage/database/mgo/friend.go index 76c82bac2..6344eecb5 100644 --- a/pkg/common/storage/database/mgo/friend.go +++ b/pkg/common/storage/database/mgo/friend.go @@ -47,6 +47,17 @@ func NewFriendMongo(db *mongo.Database) (database.Friend, error) { if err != nil { return nil, err } + // Compound index to support efficient sorted pagination: pinned friends first, then by _id. + _, err = coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{ + Keys: bson.D{ + {Key: "owner_user_id", Value: 1}, + {Key: "is_pinned", Value: -1}, + {Key: "_id", Value: 1}, + }, + }) + if err != nil { + return nil, err + } owner, err := NewVersionLog(db.Collection(database.FriendVersionName)) if err != nil { return nil, err @@ -268,3 +279,10 @@ func (f *FriendMgo) IsUpdateIsPinned(data map[string]any) bool { _, ok := data["is_pinned"] return ok } + +func (f *FriendMgo) FindPinnedFriendUserIDs(ctx context.Context, ownerUserID string) ([]string, error) { + return mongoutil.Find[string](ctx, f.coll, bson.M{ + "owner_user_id": ownerUserID, + "is_pinned": true, + }, options.Find().SetProjection(bson.M{"_id": 0, "friend_user_id": 1})) +}