From 3b50bd1b18163dfd1c2b8e59162e3b4266200cf1 Mon Sep 17 00:00:00 2001 From: hawklin2017 <32898629+hawklin2017@users.noreply.github.com> Date: Wed, 13 May 2026 11:06:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A5=BD=E5=8F=8B=E3=80=81=E7=BE=A4=E7=BD=AE?= =?UTF-8?q?=E9=A1=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/api/friend.go | 8 +++++ internal/api/group.go | 8 +++++ internal/api/router.go | 6 ++++ internal/rpc/group/group_pin.go | 49 +++++++++++++++++++++++++ internal/rpc/relation/friend_pin.go | 56 +++++++++++++++++++++++++++++ protocol | 2 +- 6 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 internal/rpc/group/group_pin.go create mode 100644 internal/rpc/relation/friend_pin.go diff --git a/internal/api/friend.go b/internal/api/friend.go index 95605b79d..14bce2388 100644 --- a/internal/api/friend.go +++ b/internal/api/friend.go @@ -134,3 +134,11 @@ func (o *FriendApi) SetMute(c *gin.Context) { func (o *FriendApi) GetMute(c *gin.Context) { a2r.Call(c, relation.FriendClient.GetMute, o.Client) } + +func (o *FriendApi) PinFriend(c *gin.Context) { + a2r.Call(c, relation.FriendClient.PinFriend, o.Client) +} + +func (o *FriendApi) UnpinFriend(c *gin.Context) { + a2r.Call(c, relation.FriendClient.UnpinFriend, o.Client) +} diff --git a/internal/api/group.go b/internal/api/group.go index 0e4bc4248..0adb9f331 100644 --- a/internal/api/group.go +++ b/internal/api/group.go @@ -193,3 +193,11 @@ func (o *GroupApi) SetGroupMute(c *gin.Context) { func (o *GroupApi) GetGroupMute(c *gin.Context) { a2r.Call(c, group.GroupClient.GetGroupMute, o.Client) } + +func (o *GroupApi) PinGroup(c *gin.Context) { + a2r.Call(c, group.GroupClient.PinGroup, o.Client) +} + +func (o *GroupApi) UnpinGroup(c *gin.Context) { + a2r.Call(c, group.GroupClient.UnpinGroup, o.Client) +} diff --git a/internal/api/router.go b/internal/api/router.go index 93366f88b..fdcef7b04 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -223,6 +223,9 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co friendRouterGroup.POST("/add_oneway_friend", f.AddOnewayFriend) friendRouterGroup.POST("/set_mute", f.SetMute) friendRouterGroup.POST("/get_mute", f.GetMute) + // 好友会话置顶 / 取消置顶(同步写入 friend.is_pinned 与 conversation.isPinned) + friendRouterGroup.POST("/pin", f.PinFriend) + friendRouterGroup.POST("/unpin", f.UnpinFriend) } g := NewGroupApi(group.NewGroupClient(groupConn)) @@ -266,6 +269,9 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co groupRouterGroup.POST("/get_group_pinned_messages", g.GetGroupPinnedMessages) groupRouterGroup.POST("/set_mute", g.SetGroupMute) groupRouterGroup.POST("/get_mute", g.GetGroupMute) + // 群会话置顶 / 取消置顶(同步写入 conversation.isPinned) + groupRouterGroup.POST("/pin", g.PinGroup) + groupRouterGroup.POST("/unpin", g.UnpinGroup) } // certificate { diff --git a/internal/rpc/group/group_pin.go b/internal/rpc/group/group_pin.go new file mode 100644 index 000000000..134c75027 --- /dev/null +++ b/internal/rpc/group/group_pin.go @@ -0,0 +1,49 @@ +package group + +import ( + "context" + + "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" + "github.com/openimsdk/protocol/constant" + pbconversation "github.com/openimsdk/protocol/conversation" + pbgroup "github.com/openimsdk/protocol/group" + "github.com/openimsdk/protocol/wrapperspb" + "github.com/openimsdk/tools/mcontext" +) + +// PinGroup pins the group conversation for the current operator +// (syncs conversation.isPinned = true via the conversation service). +func (s *groupServer) PinGroup(ctx context.Context, req *pbgroup.PinGroupReq) (*pbgroup.PinGroupResp, error) { + if err := s.setGroupConversationPinned(ctx, req.GroupID, true); err != nil { + return nil, err + } + return &pbgroup.PinGroupResp{}, nil +} + +// UnpinGroup unpins the group conversation for the current operator +// (syncs conversation.isPinned = false via the conversation service). +func (s *groupServer) UnpinGroup(ctx context.Context, req *pbgroup.UnpinGroupReq) (*pbgroup.UnpinGroupResp, error) { + if err := s.setGroupConversationPinned(ctx, req.GroupID, false); err != nil { + return nil, err + } + return &pbgroup.UnpinGroupResp{}, nil +} + +func (s *groupServer) setGroupConversationPinned(ctx context.Context, groupID string, pinned bool) error { + opUserID := mcontext.GetOpUserID(ctx) + if opUserID == "" { + return servererrs.ErrNoPermission.WrapMsg("op user id is empty") + } + // Must be a member of the group to pin / unpin its conversation. + if _, err := s.db.TakeGroupMember(ctx, groupID, opUserID); err != nil { + return err + } + conv := &pbconversation.ConversationReq{ + ConversationID: msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, groupID), + ConversationType: constant.ReadGroupChatType, + GroupID: groupID, + IsPinned: &wrapperspb.BoolValue{Value: pinned}, + } + return s.conversationClient.SetConversations(ctx, []string{opUserID}, conv) +} diff --git a/internal/rpc/relation/friend_pin.go b/internal/rpc/relation/friend_pin.go new file mode 100644 index 000000000..bbadf51ba --- /dev/null +++ b/internal/rpc/relation/friend_pin.go @@ -0,0 +1,56 @@ +package relation + +import ( + "context" + + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "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/wrapperspb" + "github.com/openimsdk/tools/log" +) + +// PinFriend pins a friend conversation for ownerUserID +// (syncs friend.is_pinned = true and conversation.isPinned = true). +func (s *friendServer) PinFriend(ctx context.Context, req *relation.PinFriendReq) (*relation.PinFriendResp, error) { + if err := s.setFriendConversationPinned(ctx, req.OwnerUserID, req.FriendUserID, true); err != nil { + return nil, err + } + return &relation.PinFriendResp{}, nil +} + +// UnpinFriend unpins a friend conversation for ownerUserID +// (syncs friend.is_pinned = false and conversation.isPinned = false). +func (s *friendServer) UnpinFriend(ctx context.Context, req *relation.UnpinFriendReq) (*relation.UnpinFriendResp, error) { + if err := s.setFriendConversationPinned(ctx, req.OwnerUserID, req.FriendUserID, false); err != nil { + return nil, err + } + return &relation.UnpinFriendResp{}, nil +} + +func (s *friendServer) setFriendConversationPinned(ctx context.Context, ownerUserID, friendUserID string, pinned bool) error { + if err := authverify.CheckAccessV3(ctx, ownerUserID, s.config.Share.IMAdminUserID); err != nil { + return err + } + if _, err := s.db.FindFriendsWithError(ctx, ownerUserID, []string{friendUserID}); err != nil { + return err + } + if err := s.db.UpdateFriends(ctx, ownerUserID, []string{friendUserID}, map[string]any{"is_pinned": pinned}); err != nil { + return err + } + convID := conversationutil.GenConversationIDForSingle(ownerUserID, friendUserID) + if err := s.conversationClient.SetConversations(ctx, []string{ownerUserID}, + &conversationpb.ConversationReq{ + ConversationID: convID, + ConversationType: constant.SingleChatType, + UserID: friendUserID, + IsPinned: &wrapperspb.BoolValue{Value: pinned}, + }); err != nil { + log.ZWarn(ctx, "sync conversation isPinned failed", err, + "ownerUserID", ownerUserID, "friendUserID", friendUserID, "isPinned", pinned) + } + s.notificationSender.FriendsInfoUpdateNotification(ctx, ownerUserID, []string{friendUserID}) + return nil +} diff --git a/protocol b/protocol index ab69a8df5..2e1f23fe0 160000 --- a/protocol +++ b/protocol @@ -1 +1 @@ -Subproject commit ab69a8df51841e7e70e637033c3f05b7e40bb747 +Subproject commit 2e1f23fe06d15adcabf07ef72c1e8b08bca0f2c1