From 88ca79a0ec54298f4e794c3c3a9c3312ee40765f Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Wed, 15 Nov 2023 17:44:14 +0800 Subject: [PATCH] group --- internal/rpc/group/fill.go | 59 +++--- internal/rpc/group/group.go | 306 ++++++++++++++++++++-------- pkg/common/db/controller/group.go | 11 +- pkg/rpcclient/notification/group.go | 56 +++-- 4 files changed, 291 insertions(+), 141 deletions(-) diff --git a/internal/rpc/group/fill.go b/internal/rpc/group/fill.go index 13b5f5608..2110de218 100644 --- a/internal/rpc/group/fill.go +++ b/internal/rpc/group/fill.go @@ -17,8 +17,6 @@ package group import ( "context" - "github.com/OpenIMSDK/tools/utils" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) @@ -55,34 +53,35 @@ import ( //} func (s *groupServer) PopulateGroupMember(ctx context.Context, members ...*relationtb.GroupMemberModel) error { - if len(members) == 0 { - return nil - } - emptyUserIDs := make(map[string]struct{}) - for _, member := range members { - if member.Nickname == "" || member.FaceURL == "" { - emptyUserIDs[member.UserID] = struct{}{} - } - } - if len(emptyUserIDs) > 0 { - users, err := s.User.GetPublicUserInfoMap(ctx, utils.Keys(emptyUserIDs), true) - if err != nil { - return err - } - for i, member := range members { - user, ok := users[member.UserID] - if !ok { - continue - } - if member.Nickname == "" { - members[i].Nickname = user.Nickname - } - if member.FaceURL == "" { - members[i].FaceURL = user.FaceURL - } - } - } - return nil + return s.Notification.PopulateGroupMember(ctx, members...) + //if len(members) == 0 { + // return nil + //} + //emptyUserIDs := make(map[string]struct{}) + //for _, member := range members { + // if member.Nickname == "" || member.FaceURL == "" { + // emptyUserIDs[member.UserID] = struct{}{} + // } + //} + //if len(emptyUserIDs) > 0 { + // users, err := s.User.GetPublicUserInfoMap(ctx, utils.Keys(emptyUserIDs), true) + // if err != nil { + // return err + // } + // for i, member := range members { + // user, ok := users[member.UserID] + // if !ok { + // continue + // } + // if member.Nickname == "" { + // members[i].Nickname = user.Nickname + // } + // if member.FaceURL == "" { + // members[i].FaceURL = user.FaceURL + // } + // } + //} + //return nil } //func (s *groupServer) TakeGroupMembers(ctx context.Context, groupID string, userID string) (*relationtb.GroupMemberModel, error) { diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index b2b1d16cc..02d6ae314 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -130,11 +130,13 @@ type groupServer struct { func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) { defer log.ZDebug(ctx, "return") - - members, err := s.db.FindGroupMember(ctx, nil, []string{req.UserID}, nil) + members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID) if err != nil { return nil, err } + if err := s.PopulateGroupMember(ctx, members...); err != nil { + return nil, err + } groupIDs := make([]string, 0, len(members)) for _, member := range members { if member.Nickname != "" && member.FaceURL != "" { @@ -1231,103 +1233,232 @@ func (s *groupServer) CancelMuteGroup(ctx context.Context, req *pbgroup.CancelMu return resp, nil } +//func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGroupMemberInfoReq) (*pbgroup.SetGroupMemberInfoResp, error) { +// resp := &pbgroup.SetGroupMemberInfoResp{} +// if len(req.Members) == 0 { +// return nil, errs.ErrArgs.Wrap("members empty") +// } +// for i := range req.Members { +// req.Members[i].FaceURL = nil +// } +// duplicateMap := make(map[[2]string]struct{}) +// userIDMap := make(map[string]struct{}) +// groupIDMap := make(map[string]struct{}) +// for _, member := range req.Members { +// key := [...]string{member.GroupID, member.UserID} +// if _, ok := duplicateMap[key]; ok { +// return nil, errs.ErrArgs.Wrap("group user duplicate") +// } +// duplicateMap[key] = struct{}{} +// userIDMap[member.UserID] = struct{}{} +// groupIDMap[member.GroupID] = struct{}{} +// } +// groupIDs := utils.Keys(groupIDMap) +// userIDs := utils.Keys(userIDMap) +// // todo +// members, err := s.FindGroupMember(ctx, groupIDs, append(userIDs, mcontext.GetOpUserID(ctx)), nil) +// if err != nil { +// return nil, err +// } +// for _, member := range members { +// delete(duplicateMap, [...]string{member.GroupID, member.UserID}) +// } +// if len(duplicateMap) > 0 { +// return nil, errs.ErrArgs.Wrap("user not found" + strings.Join(utils.Slice(utils.Keys(duplicateMap), func(e [2]string) string { +// return fmt.Sprintf("[group: %s user: %s]", e[0], e[1]) +// }), ",")) +// } +// memberMap := utils.SliceToMap(members, func(e *relationtb.GroupMemberModel) [2]string { +// return [...]string{e.GroupID, e.UserID} +// }) +// if !authverify.IsAppManagerUid(ctx) { +// opUserID := mcontext.GetOpUserID(ctx) +// for _, member := range req.Members { +// if member.RoleLevel != nil { +// switch member.RoleLevel.Value { +// case constant.GroupOrdinaryUsers, constant.GroupAdmin: +// default: +// return nil, errs.ErrArgs.Wrap("invalid role level") +// } +// } +// opMember, ok := memberMap[[...]string{member.GroupID, opUserID}] +// if !ok { +// return nil, errs.ErrArgs.Wrap(fmt.Sprintf("user %s not in group %s", opUserID, member.GroupID)) +// } +// if member.UserID == opUserID { +// if member.RoleLevel != nil { +// return nil, errs.ErrNoPermission.Wrap("can not change self role level") +// } +// continue +// } +// if opMember.RoleLevel == constant.GroupOrdinaryUsers { +// return nil, errs.ErrNoPermission.Wrap("ordinary users can not change other role level") +// } +// dbMember, ok := memberMap[[...]string{member.GroupID, member.UserID}] +// if !ok { +// return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("user %s not in group %s", member.UserID, member.GroupID)) +// } +// //if opMember.RoleLevel == constant.GroupOwner { +// // continue +// //} +// //if dbMember.RoleLevel == constant.GroupOwner { +// // return nil, errs.ErrNoPermission.Wrap("change group owner") +// //} +// //if opMember.RoleLevel == constant.GroupAdmin && dbMember.RoleLevel == constant.GroupAdmin { +// // return nil, errs.ErrNoPermission.Wrap("admin can not change other admin role info") +// //} +// switch opMember.RoleLevel { +// case constant.GroupOrdinaryUsers: +// return nil, errs.ErrNoPermission.Wrap("ordinary users can not change other role level") +// case constant.GroupAdmin: +// if dbMember.RoleLevel != constant.GroupOrdinaryUsers { +// return nil, errs.ErrNoPermission.Wrap("admin can not change other role level") +// } +// if member.RoleLevel != nil { +// return nil, errs.ErrNoPermission.Wrap("admin can not change other role level") +// } +// case constant.GroupOwner: +// //if member.RoleLevel != nil && member.RoleLevel.Value == constant.GroupOwner { +// // return nil, errs.ErrNoPermission.Wrap("owner only one") +// //} +// } +// } +// } +// for _, member := range req.Members { +// if member.RoleLevel == nil { +// continue +// } +// if memberMap[[...]string{member.GroupID, member.UserID}].RoleLevel == constant.GroupOwner { +// return nil, errs.ErrArgs.Wrap(fmt.Sprintf("group %s user %s is owner", member.GroupID, member.UserID)) +// } +// } +// for i := 0; i < len(req.Members); i++ { +// if err := CallbackBeforeSetGroupMemberInfo(ctx, req.Members[i]); err != nil { +// return nil, err +// } +// } +// if err = s.db.UpdateGroupMembers(ctx, utils.Slice(req.Members, func(e *pbgroup.SetGroupMemberInfo) *relationtb.BatchUpdateGroupMember { +// return &relationtb.BatchUpdateGroupMember{ +// GroupID: e.GroupID, +// UserID: e.UserID, +// Map: UpdateGroupMemberMap(e), +// } +// })); err != nil { +// return nil, err +// } +// for _, member := range req.Members { +// if member.RoleLevel != nil { +// switch member.RoleLevel.Value { +// case constant.GroupAdmin: +// s.Notification.GroupMemberSetToAdminNotification(ctx, member.GroupID, member.UserID) +// case constant.GroupOrdinaryUsers: +// s.Notification.GroupMemberSetToOrdinaryUserNotification(ctx, member.GroupID, member.UserID) +// } +// } +// if member.Nickname != nil || member.FaceURL != nil || member.Ex != nil { +// log.ZDebug(ctx, "setGroupMemberInfo notification", "member", member.UserID) +// if err := s.Notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID); err != nil { +// log.ZError(ctx, "setGroupMemberInfo notification failed", err, "member", member.UserID, "groupID", member.GroupID) +// } +// } +// } +// return resp, nil +//} + +func (s *groupServer) setGroupMemberInfo(ctx context.Context, memberCache map[string]*relationtb.GroupMemberModel, member *pbgroup.SetGroupMemberInfo) error { + + return nil +} + func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGroupMemberInfoReq) (*pbgroup.SetGroupMemberInfoResp, error) { resp := &pbgroup.SetGroupMemberInfoResp{} if len(req.Members) == 0 { return nil, errs.ErrArgs.Wrap("members empty") } + opUserID := mcontext.GetOpUserID(ctx) + if opUserID == "" { + return nil, errs.ErrNoPermission.Wrap("no op user id") + } + isAppManagerUid := authverify.IsAppManagerUid(ctx) for i := range req.Members { req.Members[i].FaceURL = nil } - duplicateMap := make(map[[2]string]struct{}) - userIDMap := make(map[string]struct{}) - groupIDMap := make(map[string]struct{}) - for _, member := range req.Members { - key := [...]string{member.GroupID, member.UserID} - if _, ok := duplicateMap[key]; ok { - return nil, errs.ErrArgs.Wrap("group user duplicate") - } - duplicateMap[key] = struct{}{} - userIDMap[member.UserID] = struct{}{} - groupIDMap[member.GroupID] = struct{}{} - } - groupIDs := utils.Keys(groupIDMap) - userIDs := utils.Keys(userIDMap) - // todo - members, err := s.FindGroupMember(ctx, groupIDs, append(userIDs, mcontext.GetOpUserID(ctx)), nil) - if err != nil { - return nil, err - } - for _, member := range members { - delete(duplicateMap, [...]string{member.GroupID, member.UserID}) - } - if len(duplicateMap) > 0 { - return nil, errs.ErrArgs.Wrap("user not found" + strings.Join(utils.Slice(utils.Keys(duplicateMap), func(e [2]string) string { - return fmt.Sprintf("[group: %s user: %s]", e[0], e[1]) - }), ",")) - } - memberMap := utils.SliceToMap(members, func(e *relationtb.GroupMemberModel) [2]string { - return [...]string{e.GroupID, e.UserID} - }) - if !authverify.IsAppManagerUid(ctx) { - opUserID := mcontext.GetOpUserID(ctx) - for _, member := range req.Members { - if member.RoleLevel != nil { - switch member.RoleLevel.Value { - case constant.GroupOrdinaryUsers, constant.GroupAdmin: - default: - return nil, errs.ErrArgs.Wrap("invalid role level") - } + groupMembers := make(map[string][]*pbgroup.SetGroupMemberInfo) + for i, member := range req.Members { + if member.RoleLevel != nil { + switch member.RoleLevel.Value { + case constant.GroupOwner: + return nil, errs.ErrNoPermission.Wrap("cannot set ungroup owner") + case constant.GroupAdmin, constant.GroupOrdinaryUsers: + default: + return nil, errs.ErrArgs.Wrap("invalid role level") } - opMember, ok := memberMap[[...]string{member.GroupID, opUserID}] - if !ok { - return nil, errs.ErrArgs.Wrap(fmt.Sprintf("user %s not in group %s", opUserID, member.GroupID)) + } + groupMembers[member.GroupID] = append(groupMembers[member.GroupID], req.Members[i]) + } + for groupID, members := range groupMembers { + temp := make(map[string]struct{}) + userIDs := make([]string, len(members)+1) + for _, member := range members { + if _, ok := temp[member.UserID]; ok { + return nil, errs.ErrArgs.Wrap(fmt.Sprintf("repeat group %s user %s", member.GroupID, member.UserID)) } + temp[member.UserID] = struct{}{} + userIDs = append(userIDs, member.UserID) + } + if _, ok := temp[opUserID]; !ok { + userIDs = append(userIDs, opUserID) + } + dbMembers, err := s.db.FindGroupMembers(ctx, groupID, userIDs) + if err != nil { + return nil, err + } + opUserIndex := -1 + for i, member := range dbMembers { if member.UserID == opUserID { - if member.RoleLevel != nil { - return nil, errs.ErrNoPermission.Wrap("can not change self role level") + opUserIndex = i + break + } + } + switch len(userIDs) - len(dbMembers) { + case 0: + if !isAppManagerUid { + roleLevel := dbMembers[opUserIndex].RoleLevel + if roleLevel != constant.GroupOwner { + switch roleLevel { + case constant.GroupAdmin: + for _, member := range dbMembers { + if member.RoleLevel == constant.GroupOwner { + return nil, errs.ErrNoPermission.Wrap("admin can not change group owner") + } + if member.RoleLevel == constant.GroupAdmin && member.UserID != opUserID { + return nil, errs.ErrNoPermission.Wrap("admin can not change other group admin") + } + } + case constant.GroupOrdinaryUsers: + for _, member := range dbMembers { + if !(member.RoleLevel == constant.GroupOrdinaryUsers && member.UserID == opUserID) { + return nil, errs.ErrNoPermission.Wrap("ordinary users can not change other role level") + } + } + default: + for _, member := range dbMembers { + if member.RoleLevel >= roleLevel { + return nil, errs.ErrNoPermission.Wrap("can not change higher role level") + } + } + } } - continue } - if opMember.RoleLevel == constant.GroupOrdinaryUsers { - return nil, errs.ErrNoPermission.Wrap("ordinary users can not change other role level") + case 1: + if opUserIndex >= 0 { + return nil, errs.ErrArgs.Wrap("user not in group") } - dbMember, ok := memberMap[[...]string{member.GroupID, member.UserID}] - if !ok { - return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("user %s not in group %s", member.UserID, member.GroupID)) - } - //if opMember.RoleLevel == constant.GroupOwner { - // continue - //} - //if dbMember.RoleLevel == constant.GroupOwner { - // return nil, errs.ErrNoPermission.Wrap("change group owner") - //} - //if opMember.RoleLevel == constant.GroupAdmin && dbMember.RoleLevel == constant.GroupAdmin { - // return nil, errs.ErrNoPermission.Wrap("admin can not change other admin role info") - //} - switch opMember.RoleLevel { - case constant.GroupOrdinaryUsers: - return nil, errs.ErrNoPermission.Wrap("ordinary users can not change other role level") - case constant.GroupAdmin: - if dbMember.RoleLevel != constant.GroupOrdinaryUsers { - return nil, errs.ErrNoPermission.Wrap("admin can not change other role level") - } - if member.RoleLevel != nil { - return nil, errs.ErrNoPermission.Wrap("admin can not change other role level") - } - case constant.GroupOwner: - //if member.RoleLevel != nil && member.RoleLevel.Value == constant.GroupOwner { - // return nil, errs.ErrNoPermission.Wrap("owner only one") - //} + if !isAppManagerUid { + return nil, errs.ErrNoPermission.Wrap("user not in group") } - } - } - for _, member := range req.Members { - if member.RoleLevel == nil { - continue - } - if memberMap[[...]string{member.GroupID, member.UserID}].RoleLevel == constant.GroupOwner { - return nil, errs.ErrArgs.Wrap(fmt.Sprintf("group %s user %s is owner", member.GroupID, member.UserID)) + default: + return nil, errs.ErrArgs.Wrap("user not in group") } } for i := 0; i < len(req.Members); i++ { @@ -1335,7 +1466,7 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr return nil, err } } - if err = s.db.UpdateGroupMembers(ctx, utils.Slice(req.Members, func(e *pbgroup.SetGroupMemberInfo) *relationtb.BatchUpdateGroupMember { + if err := s.db.UpdateGroupMembers(ctx, utils.Slice(req.Members, func(e *pbgroup.SetGroupMemberInfo) *relationtb.BatchUpdateGroupMember { return &relationtb.BatchUpdateGroupMember{ GroupID: e.GroupID, UserID: e.UserID, @@ -1354,10 +1485,7 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr } } if member.Nickname != nil || member.FaceURL != nil || member.Ex != nil { - log.ZDebug(ctx, "setGroupMemberInfo notification", "member", member.UserID) - if err := s.Notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID); err != nil { - log.ZError(ctx, "setGroupMemberInfo notification failed", err, "member", member.UserID, "groupID", member.GroupID) - } + s.Notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID) } } return resp, nil diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go index bb2c47557..041a04967 100644 --- a/pkg/common/db/controller/group.go +++ b/pkg/common/db/controller/group.go @@ -35,12 +35,10 @@ type GroupDatabase interface { CreateGroup(ctx context.Context, groups []*relationtb.GroupModel, groupMembers []*relationtb.GroupMemberModel) error TakeGroup(ctx context.Context, groupID string) (group *relationtb.GroupModel, err error) FindGroup(ctx context.Context, groupIDs []string) (groups []*relationtb.GroupModel, err error) - //FindNotDismissedGroup(ctx context.Context, groupIDs []string) (groups []*relationtb.GroupModel, err error) SearchGroup(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*relationtb.GroupModel, error) UpdateGroup(ctx context.Context, groupID string, data map[string]any) error DismissGroup(ctx context.Context, groupID string, deleteMember bool) error // 解散群,并删除群成员 - //GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) - // GroupMember + TakeGroupMember(ctx context.Context, groupID string, userID string) (groupMember *relationtb.GroupMemberModel, err error) TakeGroupOwner(ctx context.Context, groupID string) (*relationtb.GroupMemberModel, error) FindGroupMembers(ctx context.Context, groupID string, userIDs []string) (groupMembers []*relationtb.GroupMemberModel, err error) // * @@ -48,7 +46,6 @@ type GroupDatabase interface { FindGroupMemberRoleLevels(ctx context.Context, groupID string, roleLevels []int32) (groupMembers []*relationtb.GroupMemberModel, err error) // * FindGroupMemberAll(ctx context.Context, groupID string) (groupMembers []*relationtb.GroupMemberModel, err error) // * FindGroupsOwner(ctx context.Context, groupIDs []string) ([]*relationtb.GroupMemberModel, error) - FindGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) ([]*relationtb.GroupMemberModel, error) FindGroupMemberUserID(ctx context.Context, groupID string) ([]string, error) FindGroupMemberNum(ctx context.Context, groupID string) (uint32, error) FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) @@ -98,10 +95,6 @@ type groupDatabase struct { cache cache.GroupCache } -//func (g *groupDatabase) GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) { -// return g.groupDB.GetGroupIDsByGroupType(ctx, groupType) -//} - func (g *groupDatabase) FindGroupMemberUserID(ctx context.Context, groupID string) ([]string, error) { return g.cache.GetGroupMemberIDs(ctx, groupID) } @@ -282,7 +275,7 @@ func (g *groupDatabase) PageGetGroupMember( return uint32(len(groupMemberIDs)), members, nil } -func (g *groupDatabase) SearchGroupMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pagination pagination.Pagination) (int64, []*relationtb.GroupMemberModel, error) { +func (g *groupDatabase) SearchGroupMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (int64, []*relationtb.GroupMemberModel, error) { return g.groupMemberDB.SearchMember(ctx, keyword, groupIDs, userIDs, roleLevels, pagination) } diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index 8e71f61c3..97e18d5ef 100755 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -52,6 +52,41 @@ type GroupNotificationSender struct { db controller.GroupDatabase } +func (g *GroupNotificationSender) PopulateGroupMember(ctx context.Context, members ...*relation.GroupMemberModel) error { + if len(members) == 0 { + return nil + } + emptyUserIDs := make(map[string]struct{}) + for _, member := range members { + if member.Nickname == "" || member.FaceURL == "" { + emptyUserIDs[member.UserID] = struct{}{} + } + } + if len(emptyUserIDs) > 0 { + users, err := g.getUsersInfo(ctx, utils.Keys(emptyUserIDs)) + if err != nil { + return err + } + userMap := make(map[string]CommonUser) + for i, user := range users { + userMap[user.GetUserID()] = users[i] + } + for i, member := range members { + user, ok := userMap[member.UserID] + if !ok { + continue + } + if member.Nickname == "" { + members[i].Nickname = user.GetNickname() + } + if member.FaceURL == "" { + members[i].FaceURL = user.GetFaceURL() + } + } + } + return nil +} + func (g *GroupNotificationSender) getUser(ctx context.Context, userID string) (*sdkws.PublicUserInfo, error) { users, err := g.getUsersInfo(ctx, []string{userID}) if err != nil { @@ -103,10 +138,13 @@ func (g *GroupNotificationSender) getGroupInfo(ctx context.Context, groupID stri } func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID string, userIDs []string) ([]*sdkws.GroupMemberFullInfo, error) { - members, err := g.db.FindGroupMember(ctx, []string{groupID}, userIDs, nil) + members, err := g.db.FindGroupMembers(ctx, groupID, userIDs) if err != nil { return nil, err } + if err := g.PopulateGroupMember(ctx, members...); err != nil { + return nil, err + } log.ZDebug(ctx, "getGroupMembers", "members", members) users, err := g.getUsersInfoMap(ctx, userIDs) if err != nil { @@ -125,17 +163,6 @@ func (g *GroupNotificationSender) getGroupMembers(ctx context.Context, groupID s res = append(res, g.groupMemberDB2PB(member, user.AppMangerLevel)) delete(users, member.UserID) } - //for userID, info := range users { - // if info.AppMangerLevel == constant.AppAdmin { - // res = append(res, &sdkws.GroupMemberFullInfo{ - // GroupID: groupID, - // UserID: userID, - // Nickname: info.Nickname, - // FaceURL: info.FaceURL, - // AppMangerLevel: info.AppMangerLevel, - // }) - // } - //} return res, nil } @@ -163,10 +190,13 @@ func (g *GroupNotificationSender) getGroupMember(ctx context.Context, groupID st } func (g *GroupNotificationSender) getGroupOwnerAndAdminUserID(ctx context.Context, groupID string) ([]string, error) { - members, err := g.db.FindGroupMember(ctx, []string{groupID}, nil, []int32{constant.GroupOwner, constant.GroupAdmin}) + members, err := g.db.FindGroupMemberRoleLevels(ctx, groupID, []int32{constant.GroupOwner, constant.GroupAdmin}) if err != nil { return nil, err } + if err := g.PopulateGroupMember(ctx, members...); err != nil { + return nil, err + } fn := func(e *relation.GroupMemberModel) string { return e.UserID } return utils.Slice(members, fn), nil }