diff --git a/internal/rpc/group/copy.go b/internal/rpc/group/copy.go index 95d9d9429..d0f8102b5 100644 --- a/internal/rpc/group/copy.go +++ b/internal/rpc/group/copy.go @@ -39,3 +39,37 @@ func PbToDbGroupRequest(req *pbGroup.GroupApplicationResponseReq, handleUserID s HandledTime: time.Now(), } } + +func PbToDbMapGroupInfoForSet(group *open_im_sdk.GroupInfoForSet) map[string]any { + m := make(map[string]any) + if group.GroupName != "" { + m["group_name"] = group.GroupName + } + if group.Notification != "" { + m["notification"] = group.Notification + } + if group.Introduction != "" { + m["introduction"] = group.Introduction + } + if group.FaceURL != "" { + m["face_url"] = group.FaceURL + } + if group.NeedVerification != nil { + m["need_verification"] = group.NeedVerification.Value + } + if group.LookMemberInfo != nil { + m["look_member_info"] = group.LookMemberInfo.Value + } + if group.ApplyMemberFriend != nil { + m["apply_member_friend"] = group.ApplyMemberFriend.Value + } + return m +} + +func DbToBpCMSGroup(m *relation.GroupModel, ownerUserID string, ownerUserName string, memberCount uint32) *pbGroup.CMSGroup { + return &pbGroup.CMSGroup{ + GroupInfo: DbToPbGroupInfo(m, ownerUserID, memberCount), + GroupOwnerUserID: ownerUserID, + GroupOwnerUserName: ownerUserName, + } +} diff --git a/internal/rpc/group/g.go b/internal/rpc/group/g.go index 6be1fa8c0..079facbf8 100644 --- a/internal/rpc/group/g.go +++ b/internal/rpc/group/g.go @@ -1,7 +1,9 @@ package group import ( + "Open_IM/pkg/common/constant" "Open_IM/pkg/common/tracelog" + pbConversation "Open_IM/pkg/proto/conversation" sdk_ws "Open_IM/pkg/proto/sdk_ws" "Open_IM/pkg/utils" "context" @@ -62,6 +64,24 @@ func GetPublicUserInfoMap(ctx context.Context, userIDs []string) (map[string]*sd }), nil } +func GroupNotification(ctx context.Context, groupID string) { + var conversationReq pbConversation.ModifyConversationFieldReq + conversation := pbConversation.Conversation{ + OwnerUserID: tracelog.GetOpUserID(ctx), + ConversationID: utils.GetConversationIDBySessionType(groupID, constant.GroupChatType), + ConversationType: constant.GroupChatType, + GroupID: groupID, + } + conversationReq.Conversation = &conversation + conversationReq.OperationID = tracelog.GetOperationID(ctx) + conversationReq.FieldType = constant.FieldGroupAtType + conversation.GroupAtType = constant.GroupNotification + conversationReq.UserIDList = cacheResp.UserIDList + + _, err = pbConversation.NewConversationClient(s.etcdConn.GetConn("", config.Config.RpcRegisterName.OpenImConversationName)).ModifyConversationField(ctx, &conversationReq) + tracelog.SetCtxInfo(ctx, "ModifyConversationField", err, "req", &conversationReq, "resp", conversationReply) +} + func genGroupID(ctx context.Context, groupID string) string { if groupID != "" { return groupID diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index a70a161af..a6a8c83d9 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -23,7 +23,6 @@ import ( pbUser "Open_IM/pkg/proto/user" "Open_IM/pkg/utils" "context" - "errors" grpcPrometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "net" "strconv" @@ -32,7 +31,6 @@ import ( "google.golang.org/grpc" "google.golang.org/protobuf/types/known/wrapperspb" - "gorm.io/gorm" ) type groupServer struct { @@ -733,133 +731,81 @@ func (s *groupServer) QuitGroup(ctx context.Context, req *pbGroup.QuitGroupReq) func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbGroup.SetGroupInfoReq) (*pbGroup.SetGroupInfoResp, error) { resp := &pbGroup.SetGroupInfoResp{} - - if !hasAccess(req) { - return nil, utils.Wrap(constant.ErrIdentity, "") + if !token_verify.IsAppManagerUid(ctx) { + groupMember, err := s.GroupInterface.TakeGroupMemberByID(ctx, req.GroupInfoForSet.GroupID, tracelog.GetOpUserID(ctx)) + if err != nil { + return nil, err + } + if !(groupMember.RoleLevel == constant.GroupOwner || groupMember.RoleLevel == constant.GroupAdmin) { + return nil, constant.ErrNoPermission.Wrap("no group owner or admin") + } } - group, err := relation.GetGroupInfoByGroupID(req.GroupInfoForSet.GroupID) + group, err := s.TakeGroupByID(ctx, req.GroupInfoForSet.GroupID) if err != nil { return nil, err } if group.Status == constant.GroupStatusDismissed { return nil, utils.Wrap(constant.ErrDismissedAlready, "") } - - var changedType int32 - groupName := "" - notification := "" - introduction := "" - faceURL := "" - if group.GroupName != req.GroupInfoForSet.GroupName && req.GroupInfoForSet.GroupName != "" { - changedType = 1 - groupName = req.GroupInfoForSet.GroupName + data := PbToDbMapGroupInfoForSet(req.GroupInfoForSet) + if len(data) > 0 { + return resp, nil } - if group.Notification != req.GroupInfoForSet.Notification && req.GroupInfoForSet.Notification != "" { - changedType = changedType | (1 << 1) - notification = req.GroupInfoForSet.Notification - } - if group.Introduction != req.GroupInfoForSet.Introduction && req.GroupInfoForSet.Introduction != "" { - changedType = changedType | (1 << 2) - introduction = req.GroupInfoForSet.Introduction - } - if group.FaceURL != req.GroupInfoForSet.FaceURL && req.GroupInfoForSet.FaceURL != "" { - changedType = changedType | (1 << 3) - faceURL = req.GroupInfoForSet.FaceURL - } - - if req.GroupInfoForSet.NeedVerification != nil { - changedType = changedType | (1 << 4) - m := make(map[string]interface{}) - m["need_verification"] = req.GroupInfoForSet.NeedVerification.Value - if err := relation.UpdateGroupInfoDefaultZero(req.GroupInfoForSet.GroupID, m); err != nil { - return nil, err - } - } - if req.GroupInfoForSet.LookMemberInfo != nil { - changedType = changedType | (1 << 5) - m := make(map[string]interface{}) - m["look_member_info"] = req.GroupInfoForSet.LookMemberInfo.Value - if err := relation.UpdateGroupInfoDefaultZero(req.GroupInfoForSet.GroupID, m); err != nil { - return nil, err - } - } - if req.GroupInfoForSet.ApplyMemberFriend != nil { - changedType = changedType | (1 << 6) - m := make(map[string]interface{}) - m["apply_member_friend"] = req.GroupInfoForSet.ApplyMemberFriend.Value - if err := relation.UpdateGroupInfoDefaultZero(req.GroupInfoForSet.GroupID, m); err != nil { - return nil, err - } - } - //only administrators can set group information - var groupInfo relation2.GroupModel - utils.CopyStructFields(&groupInfo, req.GroupInfoForSet) - if req.GroupInfoForSet.Notification != "" { - groupInfo.NotificationUserID = tracelog.GetOpUserID(ctx) - groupInfo.NotificationUpdateTime = time.Now() - } - if err := rocksCache.DelGroupInfoFromCache(ctx, req.GroupInfoForSet.GroupID); err != nil { + if err := s.GroupInterface.UpdateGroup(ctx, group.GroupID, data); err != nil { return nil, err } - err = relation.SetGroupInfo(groupInfo) + group, err = s.TakeGroupByID(ctx, req.GroupInfoForSet.GroupID) if err != nil { return nil, err } - if changedType != 0 { - chat.GroupInfoSetNotification(tracelog.GetOperationID(ctx), tracelog.GetOpUserID(ctx), req.GroupInfoForSet.GroupID, groupName, notification, introduction, faceURL, req.GroupInfoForSet.NeedVerification) - } + chat.GroupInfoSetNotification(tracelog.GetOperationID(ctx), tracelog.GetOpUserID(ctx), req.GroupInfoForSet.GroupID, group.GroupName, group.Notification, group.Introduction, group.FaceURL, req.GroupInfoForSet.NeedVerification) if req.GroupInfoForSet.Notification != "" { - //get group member user id - var conversationReq pbConversation.ModifyConversationFieldReq - conversation := pbConversation.Conversation{ - OwnerUserID: tracelog.GetOpUserID(ctx), - ConversationID: utils.GetConversationIDBySessionType(req.GroupInfoForSet.GroupID, constant.GroupChatType), - ConversationType: constant.GroupChatType, - GroupID: req.GroupInfoForSet.GroupID, - } - conversationReq.Conversation = &conversation - conversationReq.OperationID = tracelog.GetOperationID(ctx) - conversationReq.FieldType = constant.FieldGroupAtType - conversation.GroupAtType = constant.GroupNotification - conversationReq.UserIDList = cacheResp.UserIDList - - _, err = pbConversation.NewConversationClient(s.etcdConn.GetConn("", config.Config.RpcRegisterName.OpenImConversationName)).ModifyConversationField(ctx, &conversationReq) - tracelog.SetCtxInfo(ctx, "ModifyConversationField", err, "req", &conversationReq, "resp", conversationReply) + GroupNotification(ctx, group.GroupID) } return resp, nil } func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbGroup.TransferGroupOwnerReq) (*pbGroup.TransferGroupOwnerResp, error) { resp := &pbGroup.TransferGroupOwnerResp{} - - groupInfo, err := relation.GetGroupInfoByGroupID(req.GroupID) + group, err := s.GroupInterface.TakeGroupByID(ctx, req.GroupID) if err != nil { return nil, err } - if groupInfo.Status == constant.GroupStatusDismissed { + if group.Status == constant.GroupStatusDismissed { return nil, utils.Wrap(constant.ErrDismissedAlready, "") } - if req.OldOwnerUserID == req.NewOwnerUserID { - return nil, err + return nil, constant.ErrArgs.Wrap("OldOwnerUserID == NewOwnerUserID") } - err = rocksCache.DelGroupMemberInfoFromCache(ctx, req.GroupID, req.NewOwnerUserID) + members, err := s.GroupInterface.GetGroupMemberListByUserID(ctx, req.GroupID, []string{req.OldOwnerUserID, req.NewOwnerUserID}) if err != nil { return nil, err } - err = rocksCache.DelGroupMemberInfoFromCache(ctx, req.GroupID, req.OldOwnerUserID) - if err != nil { - return nil, err + memberMap := utils.SliceToMap(members, func(e *relation2.GroupMemberModel) string { return e.UserID }) + if ids := utils.Single([]string{req.OldOwnerUserID, req.NewOwnerUserID}, utils.Keys(memberMap)); len(ids) > 0 { + return nil, constant.ErrArgs.Wrap("user not in group " + strings.Join(ids, ",")) } - - groupMemberInfo := relation2.GroupMemberModel{GroupID: req.GroupID, UserID: req.OldOwnerUserID, RoleLevel: constant.GroupOrdinaryUsers} - err = relation.UpdateGroupMemberInfo(groupMemberInfo) - if err != nil { - return nil, err + newOwner := memberMap[req.NewOwnerUserID] + if newOwner == nil { + return nil, constant.ErrArgs.Wrap("NewOwnerUser not in group " + req.NewOwnerUserID) } - groupMemberInfo = relation2.GroupMemberModel{GroupID: req.GroupID, UserID: req.NewOwnerUserID, RoleLevel: constant.GroupOwner} - err = relation.UpdateGroupMemberInfo(groupMemberInfo) - if err != nil { + oldOwner := memberMap[req.OldOwnerUserID] + if token_verify.IsAppManagerUid(ctx) { + if oldOwner == nil { + oldOwner, err = s.GroupInterface.GetGroupOwnerUser(ctx, req.OldOwnerUserID) + if err != nil { + return nil, err + } + } + } else { + if oldOwner == nil { + return nil, constant.ErrArgs.Wrap("OldOwnerUser not in group " + req.NewOwnerUserID) + } + if oldOwner.GroupID != tracelog.GetOpUserID(ctx) { + return nil, constant.ErrNoPermission.Wrap(fmt.Sprintf("user %s no permission transfer group owner", tracelog.GetOpUserID(ctx))) + } + } + if err := s.GroupInterface.TransferGroupOwner(ctx, req.GroupID, req.OldOwnerUserID, req.NewOwnerUserID); err != nil { return nil, err } chat.GroupOwnerTransferredNotification(req) @@ -867,52 +813,40 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbGroup.Trans } func (s *groupServer) GetGroups(ctx context.Context, req *pbGroup.GetGroupsReq) (*pbGroup.GetGroupsResp, error) { - resp := &pbGroup.GetGroupsResp{ - Groups: []*pbGroup.CMSGroup{}, - Pagination: &open_im_sdk.ResponsePagination{CurrentPage: req.Pagination.PageNumber, ShowNumber: req.Pagination.ShowNumber}, - } - + resp := &pbGroup.GetGroupsResp{} + var ( + groups []*relation2.GroupModel + err error + ) if req.GroupID != "" { - groupInfoDB, err := relation.GetGroupInfoByGroupID(req.GroupID) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return resp, nil - } - return nil, err - } - resp.GroupNum = 1 - groupInfo := &open_im_sdk.GroupInfo{} - utils.CopyStructFields(groupInfo, groupInfoDB) - groupMember, err := relation.GetGroupOwnerInfoByGroupID(req.GroupID) - if err != nil { - return nil, err - } - memberNum, err := relation.GetGroupMembersCount(req.GroupID, "") - if err != nil { - return nil, err - } - groupInfo.MemberCount = uint32(memberNum) - groupInfo.CreateTime = uint32(groupInfoDB.CreateTime.Unix()) - resp.Groups = append(resp.Groups, &pbGroup.CMSGroup{GroupInfo: groupInfo, GroupOwnerUserName: groupMember.Nickname, GroupOwnerUserID: groupMember.UserID}) + groups, err = s.GroupInterface.FindGroupsByID(ctx, []string{req.GroupID}) + resp.GroupNum = int32(len(groups)) } else { - groups, count, err := relation.GetGroupsByName(req.GroupName, req.Pagination.PageNumber, req.Pagination.ShowNumber) - if err != nil { - tracelog.SetCtxInfo(ctx, "GetGroupsByName", err, "GroupName", req.GroupName, "PageNumber", req.Pagination.PageNumber, "ShowNumber", req.Pagination.ShowNumber) - } - for _, v := range groups { - group := &pbGroup.CMSGroup{GroupInfo: &open_im_sdk.GroupInfo{}} - utils.CopyStructFields(group.GroupInfo, v) - groupMember, err := relation.GetGroupOwnerInfoByGroupID(v.GroupID) - if err != nil { - tracelog.SetCtxInfo(ctx, "GetGroupOwnerInfoByGroupID", err, "GroupID", v.GroupID) - continue - } - group.GroupInfo.CreateTime = uint32(v.CreateTime.Unix()) - group.GroupOwnerUserID = groupMember.UserID - group.GroupOwnerUserName = groupMember.Nickname - resp.Groups = append(resp.Groups, group) - } - resp.GroupNum = int32(count) + resp.GroupNum, groups, err = s.GroupInterface.FindSearchGroup(ctx, req.GroupName, req.Pagination.PageNumber, req.Pagination.ShowNumber) + } + if err != nil { + return nil, err + } + groupIDs := utils.Slice(groups, func(e *relation2.GroupModel) string { + return e.GroupID + }) + ownerMembers, err := s.GroupInterface.FindGroupOwnerUser(ctx, groupIDs) + if err != nil { + return nil, err + } + ownerMemberMap := utils.SliceToMap(ownerMembers, func(e *relation2.GroupMemberModel) string { + return e.GroupID + }) + if ids := utils.Single(groupIDs, utils.Keys(ownerMemberMap)); len(ids) > 0 { + return nil, constant.ErrDB.Wrap("group not owner " + strings.Join(ids, ",")) + } + groupMemberNumMap, err := s.GroupInterface.GetGroupMemberNum(ctx, groupIDs) + if err != nil { + return nil, err + } + for _, group := range groups { + member := ownerMemberMap[group.GroupID] + resp.Groups = append(resp.Groups, DbToBpCMSGroup(group, member.UserID, member.Nickname, uint32(groupMemberNumMap[group.GroupID]))) } return resp, nil } diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go index 1dffd2592..8edb3401d 100644 --- a/pkg/common/db/controller/group.go +++ b/pkg/common/db/controller/group.go @@ -16,6 +16,7 @@ import ( type GroupInterface interface { FindGroupsByID(ctx context.Context, groupIDs []string) (groups []*relation2.GroupModel, err error) + FindSearchGroup(ctx context.Context, name string, pageNumber, showNumber int32) (int32, []*relation2.GroupModel, error) CreateGroup(ctx context.Context, groups []*relation2.GroupModel, groupMember []*relation2.GroupMemberModel) error DeleteGroupByIDs(ctx context.Context, groupIDs []string) error TakeGroupByID(ctx context.Context, groupID string) (group *relation2.GroupModel, err error) @@ -28,7 +29,12 @@ type GroupInterface interface { DelGroupMember(ctx context.Context, groupID string, userIDs []string) error GetGroupMemberNum(ctx context.Context, groupIDs []string) (map[string]int, error) GetGroupOwnerUserID(ctx context.Context, groupIDs []string) (map[string]string, error) + GetGroupOwnerUser(ctx context.Context, groupID string) (*relation2.GroupMemberModel, error) + FindGroupOwnerUser(ctx context.Context, groupID []string) ([]*relation2.GroupMemberModel, error) + TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string) error + GetGroupRecvApplicationList(ctx context.Context, userID string) ([]*relation2.GroupRequestModel, error) + UpdateGroup(ctx context.Context, groupID string, data map[string]any) error CreateGroupMember(ctx context.Context, groupMember []*relation2.GroupMemberModel) error CreateGroupRequest(ctx context.Context, requests []*relation2.GroupRequestModel) error