feat: incr sync version.

This commit is contained in:
Gordon
2024-06-24 17:48:33 +08:00
parent e8ccae6349
commit 88b8043224
308 changed files with 55952 additions and 59 deletions

View File

@@ -0,0 +1,97 @@
// Copyright © 2023 OpenIM SDK. 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 group
import (
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/protocol/sdkws"
)
func ServerGroupToLocalGroup(info *sdkws.GroupInfo) *model_struct.LocalGroup {
return &model_struct.LocalGroup{
GroupID: info.GroupID,
GroupName: info.GroupName,
Notification: info.Notification,
Introduction: info.Introduction,
FaceURL: info.FaceURL,
CreateTime: info.CreateTime,
Status: info.Status,
CreatorUserID: info.CreatorUserID,
GroupType: info.GroupType,
OwnerUserID: info.OwnerUserID,
MemberCount: int32(info.MemberCount),
Ex: info.Ex,
NeedVerification: info.NeedVerification,
LookMemberInfo: info.LookMemberInfo,
ApplyMemberFriend: info.ApplyMemberFriend,
NotificationUpdateTime: info.NotificationUpdateTime,
NotificationUserID: info.NotificationUserID,
//AttachedInfo: info.AttachedInfo, // TODO
}
}
func ServerGroupMemberToLocalGroupMember(info *sdkws.GroupMemberFullInfo) *model_struct.LocalGroupMember {
return &model_struct.LocalGroupMember{
GroupID: info.GroupID,
UserID: info.UserID,
Nickname: info.Nickname,
FaceURL: info.FaceURL,
RoleLevel: info.RoleLevel,
JoinTime: info.JoinTime,
JoinSource: info.JoinSource,
InviterUserID: info.InviterUserID,
MuteEndTime: info.MuteEndTime,
OperatorUserID: info.OperatorUserID,
Ex: info.Ex,
//AttachedInfo: info.AttachedInfo, // todo
}
}
func ServerGroupRequestToLocalGroupRequest(info *sdkws.GroupRequest) *model_struct.LocalGroupRequest {
return &model_struct.LocalGroupRequest{
GroupID: info.GroupInfo.GroupID,
GroupName: info.GroupInfo.GroupName,
Notification: info.GroupInfo.Notification,
Introduction: info.GroupInfo.Introduction,
GroupFaceURL: info.GroupInfo.FaceURL,
CreateTime: info.GroupInfo.CreateTime,
Status: info.GroupInfo.Status,
CreatorUserID: info.GroupInfo.CreatorUserID,
GroupType: info.GroupInfo.GroupType,
OwnerUserID: info.GroupInfo.OwnerUserID,
MemberCount: int32(info.GroupInfo.MemberCount),
UserID: info.UserInfo.UserID,
Nickname: info.UserInfo.Nickname,
UserFaceURL: info.UserInfo.FaceURL,
//Gender: info.UserInfo.Gender,
HandleResult: info.HandleResult,
ReqMsg: info.ReqMsg,
HandledMsg: info.HandleMsg,
ReqTime: info.ReqTime,
HandleUserID: info.HandleUserID,
HandledTime: info.HandleTime,
Ex: info.Ex,
//AttachedInfo: info.AttachedInfo,
JoinSource: info.JoinSource,
InviterUserID: info.InviterUserID,
}
}
func ServerGroupRequestToLocalAdminGroupRequest(info *sdkws.GroupRequest) *model_struct.LocalAdminGroupRequest {
return &model_struct.LocalAdminGroupRequest{
LocalGroupRequest: *ServerGroupRequestToLocalGroupRequest(info),
}
}

View File

@@ -0,0 +1,343 @@
// Copyright © 2023 OpenIM SDK. 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 group
import (
"context"
"github.com/openimsdk/openim-sdk-core/v3/internal/util"
"github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/db_interface"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/page"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
"github.com/openimsdk/openim-sdk-core/v3/pkg/syncer"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
)
func NewGroup(loginUserID string, db db_interface.DataBase,
conversationCh chan common.Cmd2Value) *Group {
g := &Group{
loginUserID: loginUserID,
db: db,
conversationCh: conversationCh,
}
g.initSyncer()
return g
}
// //utils.GetCurrentTimestampByMill()
type Group struct {
listener func() open_im_sdk_callback.OnGroupListener
loginUserID string
db db_interface.DataBase
groupSyncer *syncer.Syncer[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string]
groupMemberSyncer *syncer.Syncer[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string]
groupRequestSyncer *syncer.Syncer[*model_struct.LocalGroupRequest, syncer.NoResp, [2]string]
groupAdminRequestSyncer *syncer.Syncer[*model_struct.LocalAdminGroupRequest, syncer.NoResp, [2]string]
joinedSuperGroupCh chan common.Cmd2Value
heartbeatCmdCh chan common.Cmd2Value
conversationCh chan common.Cmd2Value
// memberSyncMutex sync.RWMutex
listenerForService open_im_sdk_callback.OnListenerForService
}
func (g *Group) initSyncer() {
g.groupSyncer = syncer.New2[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](
syncer.WithInsert[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(ctx context.Context, value *model_struct.LocalGroup) error {
return g.db.InsertGroup(ctx, value)
}),
syncer.WithDelete[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(ctx context.Context, value *model_struct.LocalGroup) error {
if err := g.db.DeleteGroupAllMembers(ctx, value.GroupID); err != nil {
return err
}
if err := g.db.DeleteVersionSync(ctx, g.groupAndMemberVersionTableName(), value.GroupID); err != nil {
return err
}
return g.db.DeleteGroup(ctx, value.GroupID)
}),
syncer.WithUpdate[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(ctx context.Context, server, local *model_struct.LocalGroup) error {
log.ZInfo(ctx, "groupSyncer trigger update function", "groupID", server.GroupID, "server", server, "local", local)
return g.db.UpdateGroup(ctx, server)
}),
syncer.WithUUID[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(value *model_struct.LocalGroup) string {
return value.GroupID
}),
syncer.WithNotice[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(ctx context.Context, state int, server, local *model_struct.LocalGroup) error {
switch state {
case syncer.Insert:
// when a user kicked to the group and invited to the group again, group info maybe updated,
// so conversation info need to be updated
g.listener().OnJoinedGroupAdded(utils.StructToJsonString(server))
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{
Action: constant.UpdateConFaceUrlAndNickName,
Args: common.SourceIDAndSessionType{
SourceID: server.GroupID, SessionType: constant.SuperGroupChatType,
FaceURL: server.FaceURL, Nickname: server.GroupName,
},
}, g.conversationCh)
case syncer.Delete:
g.listener().OnJoinedGroupDeleted(utils.StructToJsonString(local))
case syncer.Update:
log.ZInfo(ctx, "groupSyncer trigger update", "groupID",
server.GroupID, "data", server, "isDismissed", server.Status == constant.GroupStatusDismissed)
if server.Status == constant.GroupStatusDismissed {
if err := g.db.DeleteGroupAllMembers(ctx, server.GroupID); err != nil {
log.ZError(ctx, "delete group all members failed", err)
}
g.listener().OnGroupDismissed(utils.StructToJsonString(server))
} else {
g.listener().OnGroupInfoChanged(utils.StructToJsonString(server))
if server.GroupName != local.GroupName || local.FaceURL != server.FaceURL {
_ = common.TriggerCmdUpdateConversation(ctx, common.UpdateConNode{
Action: constant.UpdateConFaceUrlAndNickName,
Args: common.SourceIDAndSessionType{
SourceID: server.GroupID, SessionType: constant.SuperGroupChatType,
FaceURL: server.FaceURL, Nickname: server.GroupName,
},
}, g.conversationCh)
}
}
}
return nil
}),
syncer.WithBatchInsert[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(ctx context.Context, values []*model_struct.LocalGroup) error {
return g.db.BatchInsertGroup(ctx, values)
}),
syncer.WithDeleteAll[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(ctx context.Context, _ string) error {
return g.db.DeleteAllGroup(ctx)
}),
syncer.WithBatchPageReq[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(entityID string) page.PageReq {
return &group.GetJoinedGroupListReq{FromUserID: entityID,
Pagination: &sdkws.RequestPagination{ShowNumber: 100}}
}),
syncer.WithBatchPageRespConvertFunc[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](func(resp *group.GetJoinedGroupListResp) []*model_struct.LocalGroup {
return datautil.Batch(ServerGroupToLocalGroup, resp.Groups)
}),
syncer.WithReqApiRouter[*model_struct.LocalGroup, group.GetJoinedGroupListResp, string](constant.GetJoinedGroupListRouter),
)
g.groupMemberSyncer = syncer.New2[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](
syncer.WithInsert[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(ctx context.Context, value *model_struct.LocalGroupMember) error {
return g.db.InsertGroupMember(ctx, value)
}),
syncer.WithDelete[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(ctx context.Context, value *model_struct.LocalGroupMember) error {
return g.db.DeleteGroupMember(ctx, value.GroupID, value.UserID)
}),
syncer.WithUpdate[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(ctx context.Context, server, local *model_struct.LocalGroupMember) error {
return g.db.UpdateGroupMember(ctx, server)
}),
syncer.WithUUID[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(value *model_struct.LocalGroupMember) [2]string {
return [...]string{value.GroupID, value.UserID}
}),
syncer.WithNotice[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(ctx context.Context, state int, server, local *model_struct.LocalGroupMember) error {
switch state {
case syncer.Insert:
g.listener().OnGroupMemberAdded(utils.StructToJsonString(server))
// When a user is kicked and invited to the group again, group member info will be updated.
_ = common.TriggerCmdUpdateMessage(ctx,
common.UpdateMessageNode{
Action: constant.UpdateMsgFaceUrlAndNickName,
Args: common.UpdateMessageInfo{
SessionType: constant.SuperGroupChatType, UserID: server.UserID, FaceURL: server.FaceURL,
Nickname: server.Nickname, GroupID: server.GroupID,
},
}, g.conversationCh)
case syncer.Delete:
g.listener().OnGroupMemberDeleted(utils.StructToJsonString(local))
case syncer.Update:
g.listener().OnGroupMemberInfoChanged(utils.StructToJsonString(server))
if server.Nickname != local.Nickname || server.FaceURL != local.FaceURL {
_ = common.TriggerCmdUpdateMessage(ctx,
common.UpdateMessageNode{
Action: constant.UpdateMsgFaceUrlAndNickName,
Args: common.UpdateMessageInfo{
SessionType: constant.SuperGroupChatType, UserID: server.UserID, FaceURL: server.FaceURL,
Nickname: server.Nickname, GroupID: server.GroupID,
},
}, g.conversationCh)
}
}
return nil
}),
syncer.WithBatchInsert[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(ctx context.Context, values []*model_struct.LocalGroupMember) error {
return g.db.BatchInsertGroupMember(ctx, values)
}),
syncer.WithDeleteAll[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(ctx context.Context, groupID string) error {
return g.db.DeleteGroupAllMembers(ctx, groupID)
}),
syncer.WithBatchPageReq[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(entityID string) page.PageReq {
return &group.GetGroupMemberListReq{GroupID: entityID, Pagination: &sdkws.RequestPagination{ShowNumber: 100}}
}),
syncer.WithBatchPageRespConvertFunc[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](func(resp *group.GetGroupMemberListResp) []*model_struct.LocalGroupMember {
return datautil.Batch(ServerGroupMemberToLocalGroupMember, resp.Members)
}),
syncer.WithReqApiRouter[*model_struct.LocalGroupMember, group.GetGroupMemberListResp, [2]string](constant.GetGroupMemberListRouter),
)
g.groupRequestSyncer = syncer.New[*model_struct.LocalGroupRequest, syncer.NoResp, [2]string](func(ctx context.Context, value *model_struct.LocalGroupRequest) error {
return g.db.InsertGroupRequest(ctx, value)
}, func(ctx context.Context, value *model_struct.LocalGroupRequest) error {
return g.db.DeleteGroupRequest(ctx, value.GroupID, value.UserID)
}, func(ctx context.Context, server, local *model_struct.LocalGroupRequest) error {
return g.db.UpdateGroupRequest(ctx, server)
}, func(value *model_struct.LocalGroupRequest) [2]string {
return [...]string{value.GroupID, value.UserID}
}, nil, func(ctx context.Context, state int, server, local *model_struct.LocalGroupRequest) error {
switch state {
case syncer.Insert:
g.listener().OnGroupApplicationAdded(utils.StructToJsonString(server))
case syncer.Update:
switch server.HandleResult {
case constant.FriendResponseAgree:
g.listener().OnGroupApplicationAccepted(utils.StructToJsonString(server))
case constant.FriendResponseRefuse:
g.listener().OnGroupApplicationRejected(utils.StructToJsonString(server))
default:
g.listener().OnGroupApplicationAdded(utils.StructToJsonString(server))
}
}
return nil
})
g.groupAdminRequestSyncer = syncer.New[*model_struct.LocalAdminGroupRequest, syncer.NoResp, [2]string](func(ctx context.Context, value *model_struct.LocalAdminGroupRequest) error {
return g.db.InsertAdminGroupRequest(ctx, value)
}, func(ctx context.Context, value *model_struct.LocalAdminGroupRequest) error {
return g.db.DeleteAdminGroupRequest(ctx, value.GroupID, value.UserID)
}, func(ctx context.Context, server, local *model_struct.LocalAdminGroupRequest) error {
return g.db.UpdateAdminGroupRequest(ctx, server)
}, func(value *model_struct.LocalAdminGroupRequest) [2]string {
return [...]string{value.GroupID, value.UserID}
}, nil, func(ctx context.Context, state int, server, local *model_struct.LocalAdminGroupRequest) error {
switch state {
case syncer.Insert:
g.listener().OnGroupApplicationAdded(utils.StructToJsonString(server))
case syncer.Update:
switch server.HandleResult {
case constant.FriendResponseAgree:
g.listener().OnGroupApplicationAccepted(utils.StructToJsonString(server))
case constant.FriendResponseRefuse:
g.listener().OnGroupApplicationRejected(utils.StructToJsonString(server))
default:
g.listener().OnGroupApplicationAdded(utils.StructToJsonString(server))
}
}
return nil
})
}
func (g *Group) SetGroupListener(listener func() open_im_sdk_callback.OnGroupListener) {
g.listener = listener
}
func (g *Group) SetListenerForService(listener open_im_sdk_callback.OnListenerForService) {
g.listenerForService = listener
}
func (g *Group) GetGroupOwnerIDAndAdminIDList(ctx context.Context, groupID string) (ownerID string, adminIDList []string, err error) {
localGroup, err := g.db.GetGroupInfoByGroupID(ctx, groupID)
if err != nil {
return "", nil, err
}
adminIDList, err = g.db.GetGroupAdminID(ctx, groupID)
if err != nil {
return "", nil, err
}
return localGroup.OwnerUserID, adminIDList, nil
}
func (g *Group) GetGroupInfoFromLocal2Svr(ctx context.Context, groupID string) (*model_struct.LocalGroup, error) {
localGroup, err := g.db.GetGroupInfoByGroupID(ctx, groupID)
if err == nil {
return localGroup, nil
}
svrGroup, err := g.getGroupsInfoFromSvr(ctx, []string{groupID})
if err != nil {
return nil, err
}
if len(svrGroup) == 0 {
return nil, sdkerrs.ErrGroupIDNotFound.WrapMsg("server not this group")
}
return ServerGroupToLocalGroup(svrGroup[0]), nil
}
func (g *Group) GetGroupsInfoFromLocal2Svr(ctx context.Context, groupIDs ...string) (map[string]*model_struct.LocalGroup, error) {
groupMap := make(map[string]*model_struct.LocalGroup)
if len(groupIDs) == 0 {
return groupMap, nil
}
groups, err := g.db.GetGroups(ctx, groupIDs)
if err != nil {
return nil, err
}
var groupIDsNeedSync []string
localGroupIDs := datautil.Slice(groups, func(group *model_struct.LocalGroup) string {
return group.GroupID
})
for _, groupID := range groupIDs {
if !datautil.Contain(groupID, localGroupIDs...) {
groupIDsNeedSync = append(groupIDsNeedSync, groupID)
}
}
if len(groupIDsNeedSync) > 0 {
svrGroups, err := g.getGroupsInfoFromSvr(ctx, groupIDsNeedSync)
if err != nil {
return nil, err
}
for _, svrGroup := range svrGroups {
groups = append(groups, ServerGroupToLocalGroup(svrGroup))
}
}
for _, group := range groups {
groupMap[group.GroupID] = group
}
return groupMap, nil
}
func (g *Group) getGroupsInfoFromSvr(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) {
resp, err := util.CallApi[group.GetGroupsInfoResp](ctx, constant.GetGroupsInfoRouter, &group.GetGroupsInfoReq{GroupIDs: groupIDs})
if err != nil {
return nil, err
}
return resp.GroupInfos, nil
}
func (g *Group) getGroupAbstractInfoFromSvr(ctx context.Context, groupIDs []string) (*group.GetGroupAbstractInfoResp, error) {
return util.CallApi[group.GetGroupAbstractInfoResp](ctx, constant.GetGroupAbstractInfoRouter, &group.GetGroupAbstractInfoReq{GroupIDs: groupIDs})
}
func (g *Group) GetJoinedDiffusionGroupIDListFromSvr(ctx context.Context) ([]string, error) {
groups, err := g.GetServerJoinGroup(ctx)
if err != nil {
return nil, err
}
var groupIDs []string
for _, g := range groups {
if g.GroupType == constant.WorkingGroup {
groupIDs = append(groupIDs, g.GroupID)
}
}
return groupIDs, nil
}

View File

@@ -0,0 +1,245 @@
// Copyright © 2023 OpenIM SDK. 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 group
import (
"context"
"fmt"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
)
func (g *Group) DoNotification(ctx context.Context, msg *sdkws.MsgData) {
go func() {
if err := g.doNotification(ctx, msg); err != nil {
log.ZError(ctx, "DoGroupNotification failed", err)
}
}()
}
func (g *Group) doNotification(ctx context.Context, msg *sdkws.MsgData) error {
switch msg.ContentType {
case constant.GroupCreatedNotification: // 1501
var detail sdkws.GroupCreatedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
if err := g.IncrSyncJoinGroup(ctx); err != nil {
return err
}
return g.IncrSyncGroupAndMember(ctx, detail.Group.GroupID)
case constant.GroupInfoSetNotification: // 1502
var detail sdkws.GroupInfoSetTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
nil, nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.JoinGroupApplicationNotification: // 1503
var detail sdkws.JoinGroupApplicationTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
if detail.Applicant.UserID == g.loginUserID {
return g.SyncSelfGroupApplications(ctx, detail.Group.GroupID)
} else {
return g.SyncAdminGroupApplications(ctx, detail.Group.GroupID)
}
case constant.MemberQuitNotification: // 1504
var detail sdkws.MemberQuitTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
if detail.QuitUser.UserID == g.loginUserID {
return g.IncrSyncJoinGroup(ctx)
} else {
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, []*sdkws.GroupMemberFullInfo{detail.QuitUser},
nil, nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
}
case constant.GroupApplicationAcceptedNotification: // 1505
var detail sdkws.GroupApplicationAcceptedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
switch detail.ReceiverAs {
case 0:
return g.SyncAllSelfGroupApplication(ctx)
case 1:
return g.SyncAdminGroupApplications(ctx, detail.Group.GroupID)
default:
return errs.New(fmt.Sprintf("GroupApplicationAcceptedNotification ReceiverAs unknown %d", detail.ReceiverAs)).Wrap()
}
case constant.GroupApplicationRejectedNotification: // 1506
var detail sdkws.GroupApplicationRejectedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
switch detail.ReceiverAs {
case 0:
return g.SyncAllSelfGroupApplication(ctx)
case 1:
return g.SyncAdminGroupApplications(ctx, detail.Group.GroupID)
default:
return errs.New(fmt.Sprintf("GroupApplicationRejectedNotification ReceiverAs unknown %d", detail.ReceiverAs)).Wrap()
}
case constant.GroupOwnerTransferredNotification: // 1507
var detail sdkws.GroupOwnerTransferredTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
if detail.Group == nil {
return errs.New(fmt.Sprintf("group is nil, groupID: %s", detail.Group.GroupID)).Wrap()
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
[]*sdkws.GroupMemberFullInfo{detail.NewGroupOwner, detail.OldGroupOwnerInfo}, nil,
detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.MemberKickedNotification: // 1508
var detail sdkws.MemberKickedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
var self bool
for _, info := range detail.KickedUserList {
if info.UserID == g.loginUserID {
self = true
break
}
}
if self {
return g.IncrSyncJoinGroup(ctx)
} else {
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, detail.KickedUserList, nil,
nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
}
case constant.MemberInvitedNotification: // 1509
var detail sdkws.MemberInvitedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
userIDMap := datautil.SliceSetAny(detail.InvitedUserList, func(e *sdkws.GroupMemberFullInfo) string {
return e.UserID
})
//自己也是被邀请的一员
if _, ok := userIDMap[g.loginUserID]; ok {
if err := g.IncrSyncJoinGroup(ctx); err != nil {
return err
}
return g.IncrSyncGroupAndMember(ctx, detail.Group.GroupID)
} else {
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
detail.InvitedUserList, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
}
case constant.MemberEnterNotification: // 1510
var detail sdkws.MemberEnterTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
if detail.EntrantUser.UserID == g.loginUserID {
if err := g.IncrSyncJoinGroup(ctx); err != nil {
return err
}
return g.IncrSyncGroupAndMember(ctx, detail.Group.GroupID)
} else {
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
[]*sdkws.GroupMemberFullInfo{detail.EntrantUser}, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
}
case constant.GroupDismissedNotification: // 1511
var detail sdkws.GroupDismissedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
g.listener().OnGroupDismissed(utils.StructToJsonString(detail.Group))
return g.IncrSyncJoinGroup(ctx)
case constant.GroupMemberMutedNotification: // 1512
var detail sdkws.GroupMemberMutedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
[]*sdkws.GroupMemberFullInfo{detail.MutedUser}, nil, nil,
detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.GroupMemberCancelMutedNotification: // 1513
var detail sdkws.GroupMemberCancelMutedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
[]*sdkws.GroupMemberFullInfo{detail.MutedUser}, nil, nil,
detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.GroupMutedNotification: // 1514
var detail sdkws.GroupMutedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.GroupCancelMutedNotification: // 1515
var detail sdkws.GroupCancelMutedTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.GroupMemberInfoSetNotification: // 1516
var detail sdkws.GroupMemberInfoSetTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
[]*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.GroupMemberSetToAdminNotification: // 1517
var detail sdkws.GroupMemberInfoSetTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
[]*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.GroupMemberSetToOrdinaryUserNotification: // 1518
var detail sdkws.GroupMemberInfoSetTips
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
[]*sdkws.GroupMemberFullInfo{detail.ChangedUser}, nil, nil,
detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.GroupInfoSetAnnouncementNotification: // 1519
var detail sdkws.GroupInfoSetAnnouncementTips //
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil, nil,
nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
case constant.GroupInfoSetNameNotification: // 1520
var detail sdkws.GroupInfoSetNameTips //
if err := utils.UnmarshalNotificationElem(msg.Content, &detail); err != nil {
return err
}
return g.onlineSyncGroupAndMember(ctx, detail.Group.GroupID, nil,
nil, nil, detail.Group, detail.GroupMemberVersion, detail.GroupMemberVersionID)
default:
return errs.New("unknown tips type", "contentType", msg.ContentType).Wrap()
}
}

View File

@@ -0,0 +1,383 @@
// Copyright © 2023 OpenIM SDK. 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 group
import (
"context"
"time"
"github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/openim-sdk-core/v3/pkg/datafetcher"
"github.com/openimsdk/openim-sdk-core/v3/internal/util"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdk_params_callback"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
"github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/protocol/wrapperspb"
)
func (g *Group) CreateGroup(ctx context.Context, req *group.CreateGroupReq) (*sdkws.GroupInfo, error) {
if req.OwnerUserID == "" {
req.OwnerUserID = g.loginUserID
}
if req.GroupInfo.GroupType != constant.WorkingGroup {
return nil, sdkerrs.ErrGroupType
}
req.GroupInfo.CreatorUserID = g.loginUserID
resp, err := util.CallApi[group.CreateGroupResp](ctx, constant.CreateGroupRouter, req)
if err != nil {
return nil, err
}
if err := g.IncrSyncJoinGroup(ctx); err != nil {
return nil, err
}
if err := g.IncrSyncGroupAndMember(ctx, resp.GroupInfo.GroupID); err != nil {
return nil, err
}
return resp.GroupInfo, nil
}
func (g *Group) JoinGroup(ctx context.Context, groupID, reqMsg string, joinSource int32, ex string) error {
if err := util.ApiPost(ctx, constant.JoinGroupRouter, &group.JoinGroupReq{GroupID: groupID, ReqMessage: reqMsg, JoinSource: joinSource, InviterUserID: g.loginUserID, Ex: ex}, nil); err != nil {
return err
}
if err := g.SyncSelfGroupApplications(ctx, groupID); err != nil {
return err
}
return nil
}
func (g *Group) QuitGroup(ctx context.Context, groupID string) error {
if err := util.ApiPost(ctx, constant.QuitGroupRouter, &group.QuitGroupReq{GroupID: groupID}, nil); err != nil {
return err
}
return nil
}
func (g *Group) DismissGroup(ctx context.Context, groupID string) error {
if err := util.ApiPost(ctx, constant.DismissGroupRouter, &group.DismissGroupReq{GroupID: groupID}, nil); err != nil {
return err
}
return nil
}
func (g *Group) SetGroupApplyMemberFriend(ctx context.Context, groupID string, rule int32) error {
return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, ApplyMemberFriend: wrapperspb.Int32(rule)})
}
func (g *Group) SetGroupLookMemberInfo(ctx context.Context, groupID string, rule int32) error {
return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, LookMemberInfo: wrapperspb.Int32(rule)})
}
func (g *Group) SetGroupVerification(ctx context.Context, groupID string, verification int32) error {
return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{GroupID: groupID, NeedVerification: wrapperspb.Int32(verification)})
}
func (g *Group) ChangeGroupMute(ctx context.Context, groupID string, isMute bool) (err error) {
if isMute {
err = util.ApiPost(ctx, constant.MuteGroupRouter, &group.MuteGroupReq{GroupID: groupID}, nil)
} else {
err = util.ApiPost(ctx, constant.CancelMuteGroupRouter, &group.CancelMuteGroupReq{GroupID: groupID}, nil)
}
if err != nil {
return err
}
if err := g.IncrSyncGroupAndMember(ctx, groupID); err != nil {
return err
}
return nil
}
func (g *Group) ChangeGroupMemberMute(ctx context.Context, groupID, userID string, mutedSeconds int) (err error) {
if mutedSeconds == 0 {
err = util.ApiPost(ctx, constant.CancelMuteGroupMemberRouter, &group.CancelMuteGroupMemberReq{GroupID: groupID, UserID: userID}, nil)
} else {
err = util.ApiPost(ctx, constant.MuteGroupMemberRouter, &group.MuteGroupMemberReq{GroupID: groupID, UserID: userID, MutedSeconds: uint32(mutedSeconds)}, nil)
}
if err != nil {
return err
}
return nil
}
func (g *Group) TransferGroupOwner(ctx context.Context, groupID, newOwnerUserID string) error {
if err := util.ApiPost(ctx, constant.TransferGroupRouter, &group.TransferGroupOwnerReq{GroupID: groupID, OldOwnerUserID: g.loginUserID, NewOwnerUserID: newOwnerUserID}, nil); err != nil {
return err
}
if err := g.IncrSyncGroupAndMember(ctx, groupID); err != nil {
return err
}
return nil
}
func (g *Group) KickGroupMember(ctx context.Context, groupID string, reason string, userIDList []string) error {
if err := util.ApiPost(ctx, constant.KickGroupMemberRouter, &group.KickGroupMemberReq{GroupID: groupID, KickedUserIDs: userIDList, Reason: reason}, nil); err != nil {
return err
}
return g.IncrSyncGroupAndMember(ctx, groupID)
}
func (g *Group) SetGroupInfo(ctx context.Context, groupInfo *sdkws.GroupInfoForSet) error {
if err := util.ApiPost(ctx, constant.SetGroupInfoRouter, &group.SetGroupInfoReq{GroupInfoForSet: groupInfo}, nil); err != nil {
return err
}
return g.IncrSyncJoinGroup(ctx)
}
func (g *Group) SetGroupMemberInfo(ctx context.Context, groupMemberInfo *group.SetGroupMemberInfo) error {
if err := util.ApiPost(ctx, constant.SetGroupMemberInfoRouter, &group.SetGroupMemberInfoReq{Members: []*group.SetGroupMemberInfo{groupMemberInfo}}, nil); err != nil {
return err
}
return g.IncrSyncGroupAndMember(ctx, groupMemberInfo.GroupID)
}
func (g *Group) SetGroupMemberRoleLevel(ctx context.Context, groupID, userID string, roleLevel int) error {
return g.SetGroupMemberInfo(ctx, &group.SetGroupMemberInfo{GroupID: groupID, UserID: userID, RoleLevel: wrapperspb.Int32(int32(roleLevel))})
}
func (g *Group) SetGroupMemberNickname(ctx context.Context, groupID, userID string, groupMemberNickname string) error {
return g.SetGroupMemberInfo(ctx, &group.SetGroupMemberInfo{GroupID: groupID, UserID: userID, Nickname: wrapperspb.String(groupMemberNickname)})
}
func (g *Group) GetJoinedGroupList(ctx context.Context) ([]*model_struct.LocalGroup, error) {
return g.db.GetJoinedGroupListDB(ctx)
}
func (g *Group) GetJoinedGroupListPage(ctx context.Context, offset, count int32) ([]*model_struct.LocalGroup, error) {
dataFetcher := datafetcher.NewDataFetcher(
g.db,
g.groupTableName(),
g.loginUserID,
func(localGroup *model_struct.LocalGroup) string {
return localGroup.GroupID
},
func(ctx context.Context, values []*model_struct.LocalGroup) error {
return g.db.BatchInsertGroup(ctx, values)
},
func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
return g.db.GetGroups(ctx, groupIDs)
},
func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
serverGroupInfo, err := g.getGroupsInfoFromSvr(ctx, groupIDs)
if err != nil {
return nil, err
}
return datautil.Batch(ServerGroupToLocalGroup, serverGroupInfo), nil
},
)
return dataFetcher.FetchWithPagination(ctx, int(offset), int(count))
}
func (g *Group) GetSpecifiedGroupsInfo(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
dataFetcher := datafetcher.NewDataFetcher(
g.db,
g.groupTableName(),
g.loginUserID,
func(localGroup *model_struct.LocalGroup) string {
return localGroup.GroupID
},
func(ctx context.Context, values []*model_struct.LocalGroup) error {
return g.db.BatchInsertGroup(ctx, values)
},
func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
return g.db.GetGroups(ctx, groupIDs)
},
func(ctx context.Context, groupIDs []string) ([]*model_struct.LocalGroup, error) {
serverGroupInfo, err := g.getGroupsInfoFromSvr(ctx, groupIDs)
if err != nil {
return nil, err
}
return datautil.Batch(ServerGroupToLocalGroup, serverGroupInfo), nil
},
)
return dataFetcher.FetchMissingAndFillLocal(ctx, groupIDs)
}
func (g *Group) SearchGroups(ctx context.Context, param sdk_params_callback.SearchGroupsParam) ([]*model_struct.LocalGroup, error) {
if len(param.KeywordList) == 0 || (!param.IsSearchGroupName && !param.IsSearchGroupID) {
return nil, sdkerrs.ErrArgs.WrapMsg("keyword is null or search field all false")
}
groups, err := g.db.GetAllGroupInfoByGroupIDOrGroupName(ctx, param.KeywordList[0], param.IsSearchGroupID, param.IsSearchGroupName) // todo param.KeywordList[0]
if err != nil {
return nil, err
}
return groups, nil
}
// funcation (g *Group) SetGroupInfo(ctx context.Context, groupInfo *sdk_params_callback.SetGroupInfoParam, groupID string) error {
// return g.SetGroupInfo(ctx, &sdkws.GroupInfoForSet{
// GroupID: groupID,
// GroupName: groupInfo.GroupName,
// Notification: groupInfo.Notification,
// Introduction: groupInfo.Introduction,
// FaceURL: groupInfo.FaceURL,
// Ex: groupInfo.Ex,
// NeedVerification: wrapperspb.Int32Ptr(groupInfo.NeedVerification),
// })
// }
func (g *Group) GetGroupMemberOwnerAndAdmin(ctx context.Context, groupID string) ([]*model_struct.LocalGroupMember, error) {
return g.db.GetGroupMemberOwnerAndAdminDB(ctx, groupID)
}
func (g *Group) GetGroupMemberListByJoinTimeFilter(ctx context.Context, groupID string, offset, count int32, joinTimeBegin, joinTimeEnd int64, userIDs []string) ([]*model_struct.LocalGroupMember, error) {
if joinTimeEnd == 0 {
joinTimeEnd = time.Now().UnixMilli()
}
return g.db.GetGroupMemberListSplitByJoinTimeFilter(ctx, groupID, int(offset), int(count), joinTimeBegin, joinTimeEnd, userIDs)
}
func (g *Group) GetSpecifiedGroupMembersInfo(ctx context.Context, groupID string, userIDList []string) ([]*model_struct.LocalGroupMember, error) {
dataFetcher := datafetcher.NewDataFetcher(
g.db,
g.groupAndMemberVersionTableName(),
groupID,
func(localGroupMember *model_struct.LocalGroupMember) string {
return localGroupMember.UserID
},
func(ctx context.Context, values []*model_struct.LocalGroupMember) error {
return g.db.BatchInsertGroupMember(ctx, values)
},
func(ctx context.Context, userIDs []string) ([]*model_struct.LocalGroupMember, error) {
return g.db.GetGroupSomeMemberInfo(ctx, groupID, userIDList)
},
func(ctx context.Context, userIDs []string) ([]*model_struct.LocalGroupMember, error) {
serverGroupMember, err := g.GetDesignatedGroupMembers(ctx, groupID, userIDs)
if err != nil {
return nil, err
}
return datautil.Batch(ServerGroupMemberToLocalGroupMember, serverGroupMember), nil
},
)
return dataFetcher.FetchMissingAndFillLocal(ctx, userIDList)
// return g.db.GetGroupSomeMemberInfo(ctx, groupID, userIDList)
}
func (g *Group) GetGroupMemberList(ctx context.Context, groupID string, filter, offset, count int32) ([]*model_struct.LocalGroupMember, error) {
dataFetcher := datafetcher.NewDataFetcher(
g.db,
g.groupAndMemberVersionTableName(),
groupID,
func(localGroupMember *model_struct.LocalGroupMember) string {
return localGroupMember.UserID
},
func(ctx context.Context, values []*model_struct.LocalGroupMember) error {
return g.db.BatchInsertGroupMember(ctx, values)
},
func(ctx context.Context, userIDs []string) ([]*model_struct.LocalGroupMember, error) {
return g.db.GetGroupMemberListByUserIDs(ctx, groupID, filter, userIDs)
},
func(ctx context.Context, userIDs []string) ([]*model_struct.LocalGroupMember, error) {
serverGroupMember, err := g.GetDesignatedGroupMembers(ctx, groupID, userIDs)
if err != nil {
return nil, err
}
return datautil.Batch(ServerGroupMemberToLocalGroupMember, serverGroupMember), nil
},
)
return dataFetcher.FetchWithPagination(ctx, int(offset), int(count))
}
func (g *Group) GetGroupApplicationListAsRecipient(ctx context.Context) ([]*model_struct.LocalAdminGroupRequest, error) {
return g.db.GetAdminGroupApplication(ctx)
}
func (g *Group) GetGroupApplicationListAsApplicant(ctx context.Context) ([]*model_struct.LocalGroupRequest, error) {
return g.db.GetSendGroupApplication(ctx)
}
func (g *Group) SearchGroupMembers(ctx context.Context, searchParam *sdk_params_callback.SearchGroupMembersParam) ([]*model_struct.LocalGroupMember, error) {
return g.db.SearchGroupMembersDB(ctx, searchParam.KeywordList[0], searchParam.GroupID, searchParam.IsSearchMemberNickname, searchParam.IsSearchUserID, searchParam.Offset, searchParam.Count)
}
func (g *Group) IsJoinGroup(ctx context.Context, groupID string) (bool, error) {
groupList, err := g.db.GetJoinedGroupListDB(ctx)
if err != nil {
return false, err
}
for _, localGroup := range groupList {
if localGroup.GroupID == groupID {
return true, nil
}
}
return false, nil
}
func (g *Group) InviteUserToGroup(ctx context.Context, groupID, reason string, userIDList []string) error {
if err := util.ApiPost(ctx, constant.InviteUserToGroupRouter, &group.InviteUserToGroupReq{GroupID: groupID, Reason: reason, InvitedUserIDs: userIDList}, nil); err != nil {
return err
}
if err := g.IncrSyncGroupAndMember(ctx, groupID); err != nil {
return err
}
return nil
}
func (g *Group) AcceptGroupApplication(ctx context.Context, groupID, fromUserID, handleMsg string) error {
return g.HandlerGroupApplication(ctx, &group.GroupApplicationResponseReq{GroupID: groupID, FromUserID: fromUserID, HandledMsg: handleMsg, HandleResult: constant.GroupResponseAgree})
}
func (g *Group) RefuseGroupApplication(ctx context.Context, groupID, fromUserID, handleMsg string) error {
return g.HandlerGroupApplication(ctx, &group.GroupApplicationResponseReq{GroupID: groupID, FromUserID: fromUserID, HandledMsg: handleMsg, HandleResult: constant.GroupResponseRefuse})
}
func (g *Group) HandlerGroupApplication(ctx context.Context, req *group.GroupApplicationResponseReq) error {
if err := util.ApiPost(ctx, constant.AcceptGroupApplicationRouter, req, nil); err != nil {
return err
}
// SyncAdminGroupApplication todo
return nil
}
//func (g *Group) SearchGroupMembersV2(ctx context.Context, req *group.SearchGroupMemberReq) ([]*model_struct.LocalGroupMember, error) {
// if err := req.Check(); err != nil {
// return nil, err
// }
// info, err := g.db.GetGroupInfoByGroupID(ctx, req.GroupID)
// if err != nil {
// return nil, err
// }
// if info.MemberCount <= pconstant.MaxSyncPullNumber {
// return g.db.SearchGroupMembersDB(ctx, req.Keyword, req.GroupID, true, false,
// int((req.Pagination.PageNumber-1)*req.Pagination.ShowNumber), int(req.Pagination.ShowNumber))
// }
// resp, err := util.CallApi[group.SearchGroupMemberResp](ctx, constant.SearchGroupMember, req)
// if err != nil {
// return nil, err
// }
// return datautil.Slice(resp.Members, g.pbGroupMemberToLocal), nil
//}
func (g *Group) pbGroupMemberToLocal(pb *sdkws.GroupMemberFullInfo) *model_struct.LocalGroupMember {
return &model_struct.LocalGroupMember{
GroupID: pb.GroupID,
UserID: pb.UserID,
Nickname: pb.Nickname,
FaceURL: pb.FaceURL,
RoleLevel: pb.RoleLevel,
JoinTime: pb.JoinTime,
JoinSource: pb.JoinSource,
InviterUserID: pb.InviterUserID,
MuteEndTime: pb.MuteEndTime,
OperatorUserID: pb.OperatorUserID,
Ex: pb.Ex,
// AttachedInfo: pb.AttachedInfo,
}
}

View File

@@ -0,0 +1,310 @@
// Copyright © 2023 OpenIM SDK. 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 group
import (
"context"
"crypto/md5"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"github.com/openimsdk/openim-sdk-core/v3/internal/util"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
"github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
"time"
)
func (g *Group) getGroupHash(members []*model_struct.LocalGroupMember) uint64 {
userIDs := datautil.Slice(members, func(member *model_struct.LocalGroupMember) string {
return member.UserID
})
datautil.Sort(userIDs, true)
memberMap := make(map[string]*sdkws.GroupMemberFullInfo)
for _, member := range members {
memberMap[member.UserID] = &sdkws.GroupMemberFullInfo{
GroupID: member.GroupID,
UserID: member.UserID,
RoleLevel: member.RoleLevel,
JoinTime: member.JoinTime,
Nickname: member.Nickname,
FaceURL: member.FaceURL,
AppMangerLevel: 0,
JoinSource: member.JoinSource,
OperatorUserID: member.OperatorUserID,
Ex: member.Ex,
MuteEndTime: member.MuteEndTime,
InviterUserID: member.InviterUserID,
}
}
res := make([]*sdkws.GroupMemberFullInfo, 0, len(members))
for _, userID := range userIDs {
res = append(res, memberMap[userID])
}
val, _ := json.Marshal(res)
sum := md5.Sum(val)
return binary.BigEndian.Uint64(sum[:])
}
func (g *Group) SyncAllGroupMember(ctx context.Context, groupID string) error {
absInfo, err := g.GetGroupAbstractInfo(ctx, groupID)
if err != nil {
return err
}
localData, err := g.db.GetGroupMemberListSplit(ctx, groupID, 0, 0, 9999999)
if err != nil {
return err
}
hashCode := g.getGroupHash(localData)
if len(localData) == int(absInfo.GroupMemberNumber) && hashCode == absInfo.GroupMemberListHash {
log.ZDebug(ctx, "SyncAllGroupMember no change in personnel", "groupID", groupID, "hashCode", hashCode, "absInfo.GroupMemberListHash", absInfo.GroupMemberListHash)
return nil
}
members, err := g.GetServerGroupMembers(ctx, groupID)
if err != nil {
return err
}
return g.syncGroupMembers(ctx, groupID, members, localData)
}
func (g *Group) SyncAllGroupMember2(ctx context.Context, groupID string) error {
return g.IncrSyncGroupAndMember(ctx, groupID)
}
func (g *Group) syncGroupMembers(ctx context.Context, groupID string, members []*sdkws.GroupMemberFullInfo, localData []*model_struct.LocalGroupMember) error {
log.ZInfo(ctx, "SyncGroupMember Info", "groupID", groupID, "members", len(members), "localData", len(localData))
err := g.groupMemberSyncer.Sync(ctx, datautil.Batch(ServerGroupMemberToLocalGroupMember, members), localData, nil)
if err != nil {
return err
}
//if len(members) != len(localData) {
log.ZInfo(ctx, "SyncGroupMember Sync Group Member Count", "groupID", groupID, "members", len(members), "localData", len(localData))
gs, err := g.GetSpecifiedGroupsInfo(ctx, []string{groupID})
if err != nil {
return err
}
log.ZInfo(ctx, "SyncGroupMember GetGroupsInfo", "groupID", groupID, "len", len(gs), "gs", gs)
if len(gs) > 0 {
v := gs[0]
count, err := g.db.GetGroupMemberCount(ctx, groupID)
if err != nil {
return err
}
if v.MemberCount != count {
v.MemberCount = count
if v.GroupType == constant.SuperGroupChatType {
if err := g.db.UpdateSuperGroup(ctx, v); err != nil {
//return err
log.ZError(ctx, "SyncGroupMember UpdateSuperGroup", err, "groupID", groupID, "info", v)
}
} else {
if err := g.db.UpdateGroup(ctx, v); err != nil {
log.ZError(ctx, "SyncGroupMember UpdateGroup", err, "groupID", groupID, "info", v)
}
}
data, err := json.Marshal(v)
if err != nil {
return err
}
log.ZInfo(ctx, "SyncGroupMember OnGroupInfoChanged", "groupID", groupID, "data", string(data))
g.listener().OnGroupInfoChanged(string(data))
}
}
//}
return nil
}
func (g *Group) SyncGroupMembers(ctx context.Context, groupID string, userIDs ...string) error {
return g.IncrSyncGroupAndMember(ctx, groupID)
//members, err := g.GetDesignatedGroupMembers(ctx, groupID, userIDs)
//if err != nil {
// return err
//}
//localData, err := g.db.GetGroupSomeMemberInfo(ctx, groupID, userIDs)
//if err != nil {
// return err
//}
//return g.syncGroupMembers(ctx, groupID, members, localData)
}
func (g *Group) SyncGroups(ctx context.Context, groupIDs ...string) error {
return g.IncrSyncJoinGroup(ctx)
//groups, err := g.getGroupsInfoFromSvr(ctx, groupIDs)
//if err != nil {
// return err
//}
//localData, err := g.db.GetGroups(ctx, groupIDs)
//if err != nil {
// return err
//}
//if err := g.groupSyncer.Sync(ctx, util.Batch(ServerGroupToLocalGroup, groups), localData, nil); err != nil {
// return err
//}
//return nil
}
func (g *Group) deleteGroup(ctx context.Context, groupID string) error {
return g.IncrSyncJoinGroup(ctx)
//groupInfo, err := g.db.GetGroupInfoByGroupID(ctx, groupID)
//if err != nil {
// return err
//}
//if err := g.db.DeleteGroup(ctx, groupID); err != nil {
// return err
//}
//g.listener().OnJoinedGroupDeleted(utils.StructToJsonString(groupInfo))
//return nil
}
// func (g *Group) SyncAllJoinedGroupsAndMembers(ctx context.Context) error {
// t := time.Now()
// defer func(start time.Time) {
//
// elapsed := time.Since(start).Milliseconds()
// log.ZDebug(ctx, "SyncAllJoinedGroupsAndMembers fn call end", "cost time", fmt.Sprintf("%d ms", elapsed))
//
// }(t)
// _, err := g.syncAllJoinedGroups(ctx)
// if err != nil {
// return err
// }
// groups, err := g.db.GetJoinedGroupListDB(ctx)
// if err != nil {
// return err
// }
// var wg sync.WaitGroup
// for _, group := range groups {
// wg.Add(1)
// go func(groupID string) {
// defer wg.Done()
// if err := g.SyncAllGroupMember(ctx, groupID); err != nil {
// log.ZError(ctx, "SyncGroupMember failed", err)
// }
// }(group.GroupID)
// }
// wg.Wait()
// return nil
// }
func (g *Group) SyncAllJoinedGroupsAndMembers(ctx context.Context) error {
t := time.Now()
defer func(start time.Time) {
elapsed := time.Since(start).Milliseconds()
log.ZDebug(ctx, "SyncAllJoinedGroupsAndMembers fn call end", "cost time", fmt.Sprintf("%d ms", elapsed))
}(t)
if err := g.IncrSyncJoinGroup(ctx); err != nil {
return err
}
return g.IncrSyncJoinGroupMember(ctx)
}
func (g *Group) syncAllJoinedGroups(ctx context.Context) ([]*sdkws.GroupInfo, error) {
groups, err := g.GetServerJoinGroup(ctx)
if err != nil {
return nil, err
}
localData, err := g.db.GetJoinedGroupListDB(ctx)
if err != nil {
return nil, err
}
if err := g.groupSyncer.Sync(ctx, datautil.Batch(ServerGroupToLocalGroup, groups), localData, nil); err != nil {
return nil, err
}
return groups, nil
}
func (g *Group) SyncAllSelfGroupApplication(ctx context.Context) error {
list, err := g.GetServerSelfGroupApplication(ctx)
if err != nil {
return err
}
localData, err := g.db.GetSendGroupApplication(ctx)
if err != nil {
return err
}
if err := g.groupRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalGroupRequest, list), localData, nil); err != nil {
return err
}
// todo
return nil
}
func (g *Group) SyncSelfGroupApplications(ctx context.Context, groupIDs ...string) error {
return g.SyncAllSelfGroupApplication(ctx)
}
func (g *Group) SyncAllAdminGroupApplication(ctx context.Context) error {
requests, err := g.GetServerAdminGroupApplicationList(ctx)
if err != nil {
return err
}
localData, err := g.db.GetAdminGroupApplication(ctx)
if err != nil {
return err
}
return g.groupAdminRequestSyncer.Sync(ctx, datautil.Batch(ServerGroupRequestToLocalAdminGroupRequest, requests), localData, nil)
}
func (g *Group) SyncAdminGroupApplications(ctx context.Context, groupIDs ...string) error {
return g.SyncAllAdminGroupApplication(ctx)
}
func (g *Group) GetServerJoinGroup(ctx context.Context) ([]*sdkws.GroupInfo, error) {
fn := func(resp *group.GetJoinedGroupListResp) []*sdkws.GroupInfo { return resp.Groups }
req := &group.GetJoinedGroupListReq{FromUserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
return util.GetPageAll(ctx, constant.GetJoinedGroupListRouter, req, fn)
}
func (g *Group) GetServerAdminGroupApplicationList(ctx context.Context) ([]*sdkws.GroupRequest, error) {
fn := func(resp *group.GetGroupApplicationListResp) []*sdkws.GroupRequest { return resp.GroupRequests }
req := &group.GetGroupApplicationListReq{FromUserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
return util.GetPageAll(ctx, constant.GetRecvGroupApplicationListRouter, req, fn)
}
func (g *Group) GetServerSelfGroupApplication(ctx context.Context) ([]*sdkws.GroupRequest, error) {
fn := func(resp *group.GetGroupApplicationListResp) []*sdkws.GroupRequest { return resp.GroupRequests }
req := &group.GetUserReqApplicationListReq{UserID: g.loginUserID, Pagination: &sdkws.RequestPagination{}}
return util.GetPageAll(ctx, constant.GetSendGroupApplicationListRouter, req, fn)
}
func (g *Group) GetServerGroupMembers(ctx context.Context, groupID string) ([]*sdkws.GroupMemberFullInfo, error) {
req := &group.GetGroupMemberListReq{GroupID: groupID, Pagination: &sdkws.RequestPagination{}}
fn := func(resp *group.GetGroupMemberListResp) []*sdkws.GroupMemberFullInfo { return resp.Members }
return util.GetPageAll(ctx, constant.GetGroupMemberListRouter, req, fn)
}
func (g *Group) GetDesignatedGroupMembers(ctx context.Context, groupID string, userID []string) ([]*sdkws.GroupMemberFullInfo, error) {
resp := &group.GetGroupMembersInfoResp{}
if err := util.ApiPost(ctx, constant.GetGroupMembersInfoRouter, &group.GetGroupMembersInfoReq{GroupID: groupID, UserIDs: userID}, resp); err != nil {
return nil, err
}
return resp.Members, nil
}
func (g *Group) GetGroupAbstractInfo(ctx context.Context, groupID string) (*group.GroupAbstractInfo, error) {
resp, err := util.CallApi[group.GetGroupAbstractInfoResp](ctx, constant.GetGroupAbstractInfoRouter, &group.GetGroupAbstractInfoReq{GroupIDs: []string{groupID}})
if err != nil {
return nil, err
}
if len(resp.GroupAbstractInfos) == 0 {
return nil, errors.New("group not found")
}
return resp.GroupAbstractInfos[0], nil
}

View File

@@ -0,0 +1,347 @@
package group
import (
"context"
"sync"
"gorm.io/gorm"
"github.com/openimsdk/openim-sdk-core/v3/internal/incrversion"
"github.com/openimsdk/openim-sdk-core/v3/internal/util"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
constantpb "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
)
type BatchIncrementalReq struct {
UserID string `json:"user_id"`
List []*group.GetIncrementalGroupMemberReq `json:"list"`
}
type BatchIncrementalResp struct {
List map[string]*group.GetIncrementalGroupMemberResp `json:"list"`
}
func (g *Group) getIncrementalGroupMemberBatch(ctx context.Context, groups []*group.GetIncrementalGroupMemberReq) (map[string]*group.GetIncrementalGroupMemberResp, error) {
resp, err := util.CallApi[BatchIncrementalResp](ctx, constant.GetIncrementalGroupMemberBatch, &BatchIncrementalReq{UserID: g.loginUserID, List: groups})
if err != nil {
return nil, err
}
return resp.List, nil
}
func (g *Group) groupAndMemberVersionTableName() string {
return "local_group_entities_version"
}
func (g *Group) groupTableName() string {
return model_struct.LocalGroup{}.TableName()
}
func (g *Group) IncrSyncJoinGroupMember(ctx context.Context) error {
groups, err := g.db.GetJoinedGroupListDB(ctx)
if err != nil {
return err
}
groupIDs := datautil.Slice(groups, func(e *model_struct.LocalGroup) string {
return e.GroupID
})
return g.IncrSyncGroupAndMember(ctx, groupIDs...)
}
func (g *Group) IncrSyncGroupAndMember(ctx context.Context, groupIDs ...string) error {
var wg sync.WaitGroup
if len(groupIDs) == 0 {
return nil
}
const maxSyncNum = constantpb.MaxSyncPullNumber
groupIDSet := datautil.SliceSet(groupIDs)
var groups []*group.GetIncrementalGroupMemberReq
if len(groupIDs) > maxSyncNum {
groups = make([]*group.GetIncrementalGroupMemberReq, 0, maxSyncNum)
} else {
groups = make([]*group.GetIncrementalGroupMemberReq, 0, len(groupIDs))
}
for {
if len(groupIDSet) == 0 {
return nil
}
for groupID := range groupIDSet {
if len(groups) == cap(groups) {
break
}
req := group.GetIncrementalGroupMemberReq{
GroupID: groupID,
}
lvs, err := g.db.GetVersionSync(ctx, g.groupAndMemberVersionTableName(), groupID)
if err == nil {
req.VersionID = lvs.VersionID
req.Version = lvs.Version
} else if errs.Unwrap(err) != gorm.ErrRecordNotFound {
return err
}
groups = append(groups, &req)
}
groupVersion, err := g.getIncrementalGroupMemberBatch(ctx, groups)
if err != nil {
return err
}
groups = groups[:0]
for groupID, resp := range groupVersion {
tempResp := resp
tempGroupID := groupID
wg.Add(1)
go func() error {
if err := g.syncGroupAndMember(ctx, tempGroupID, tempResp); err != nil {
return err
}
wg.Done()
return nil
}()
delete(groupIDSet, tempGroupID)
}
wg.Wait()
num := len(groupIDSet)
_ = num
}
}
func (g *Group) syncGroupAndMember(ctx context.Context, groupID string, resp *group.GetIncrementalGroupMemberResp) error {
groupMemberSyncer := incrversion.VersionSynchronizer[*model_struct.LocalGroupMember, *group.GetIncrementalGroupMemberResp]{
Ctx: ctx,
DB: g.db,
TableName: g.groupAndMemberVersionTableName(),
EntityID: groupID,
Key: func(localGroupMember *model_struct.LocalGroupMember) string {
return localGroupMember.UserID
},
Local: func() ([]*model_struct.LocalGroupMember, error) {
return g.db.GetGroupMemberListByGroupID(ctx, groupID)
},
ServerVersion: func() *group.GetIncrementalGroupMemberResp {
return resp
},
Full: func(resp *group.GetIncrementalGroupMemberResp) bool {
return resp.Full
},
Version: func(resp *group.GetIncrementalGroupMemberResp) (string, uint64) {
return resp.VersionID, resp.Version
},
Delete: func(resp *group.GetIncrementalGroupMemberResp) []string {
return resp.Delete
},
Update: func(resp *group.GetIncrementalGroupMemberResp) []*model_struct.LocalGroupMember {
return datautil.Batch(ServerGroupMemberToLocalGroupMember, resp.Update)
},
Insert: func(resp *group.GetIncrementalGroupMemberResp) []*model_struct.LocalGroupMember {
return datautil.Batch(ServerGroupMemberToLocalGroupMember, resp.Insert)
},
ExtraData: func(resp *group.GetIncrementalGroupMemberResp) any {
return resp.Group
},
ExtraDataProcessor: func(ctx context.Context, data any) error {
groupInfo, ok := data.(*sdkws.GroupInfo)
if !ok {
return errs.New("group info type error")
}
if groupInfo == nil {
return nil
}
local, err := g.db.GetJoinedGroupListDB(ctx)
if err != nil {
return err
}
log.ZDebug(ctx, "group info", "groupInfo", groupInfo)
changes := datautil.Batch(ServerGroupToLocalGroup, []*sdkws.GroupInfo{groupInfo})
kv := datautil.SliceToMapAny(local, func(e *model_struct.LocalGroup) (string, *model_struct.LocalGroup) {
return e.GroupID, e
})
for i, change := range changes {
key := change.GroupID
kv[key] = changes[i]
}
server := datautil.Values(kv)
return g.groupSyncer.Sync(ctx, server, local, nil)
},
Syncer: func(server, local []*model_struct.LocalGroupMember) error {
return g.groupMemberSyncer.Sync(ctx, server, local, nil)
},
FullSyncer: func(ctx context.Context) error {
return g.groupMemberSyncer.FullSync(ctx, groupID)
},
FullID: func(ctx context.Context) ([]string, error) {
resp, err := util.CallApi[group.GetFullGroupMemberUserIDsResp](ctx, constant.GetFullGroupMemberUserIDs, &group.GetFullGroupMemberUserIDsReq{
GroupID: groupID,
})
if err != nil {
return nil, err
}
return resp.UserIDs, nil
},
}
return groupMemberSyncer.Sync()
}
func (g *Group) onlineSyncGroupAndMember(ctx context.Context, groupID string, deleteGroupMembers, updateGroupMembers, insertGroupMembers []*sdkws.GroupMemberFullInfo,
updateGroup *sdkws.GroupInfo, version uint64, versionID string) error {
groupMemberSyncer := incrversion.VersionSynchronizer[*model_struct.LocalGroupMember, *group.GetIncrementalGroupMemberResp]{
Ctx: ctx,
DB: g.db,
TableName: g.groupAndMemberVersionTableName(),
EntityID: groupID,
Key: func(localGroupMember *model_struct.LocalGroupMember) string {
return localGroupMember.UserID
},
Local: func() ([]*model_struct.LocalGroupMember, error) {
return g.db.GetGroupMemberListByGroupID(ctx, groupID)
},
ServerVersion: func() *group.GetIncrementalGroupMemberResp {
return &group.GetIncrementalGroupMemberResp{
Version: version,
VersionID: versionID,
Full: false,
Delete: datautil.Slice(deleteGroupMembers, func(e *sdkws.GroupMemberFullInfo) string {
return e.UserID
}),
Insert: insertGroupMembers,
Update: updateGroupMembers,
Group: updateGroup,
}
},
Server: func(version *model_struct.LocalVersionSync) (*group.GetIncrementalGroupMemberResp, error) {
singleGroupReq := &group.GetIncrementalGroupMemberReq{
GroupID: groupID,
VersionID: version.VersionID,
Version: version.Version,
}
resp, err := util.CallApi[BatchIncrementalResp](ctx, constant.GetIncrementalGroupMemberBatch,
&BatchIncrementalReq{UserID: g.loginUserID, List: []*group.GetIncrementalGroupMemberReq{singleGroupReq}})
if err != nil {
return nil, err
}
if resp.List != nil {
if singleGroupResp, ok := resp.List[groupID]; ok {
return singleGroupResp, nil
}
}
return nil, errs.New("group member version record not found")
},
Full: func(resp *group.GetIncrementalGroupMemberResp) bool {
return resp.Full
},
Version: func(resp *group.GetIncrementalGroupMemberResp) (string, uint64) {
return resp.VersionID, resp.Version
},
Delete: func(resp *group.GetIncrementalGroupMemberResp) []string {
return resp.Delete
},
Update: func(resp *group.GetIncrementalGroupMemberResp) []*model_struct.LocalGroupMember {
return datautil.Batch(ServerGroupMemberToLocalGroupMember, resp.Update)
},
Insert: func(resp *group.GetIncrementalGroupMemberResp) []*model_struct.LocalGroupMember {
return datautil.Batch(ServerGroupMemberToLocalGroupMember, resp.Insert)
},
ExtraData: func(resp *group.GetIncrementalGroupMemberResp) any {
return resp.Group
},
ExtraDataProcessor: func(ctx context.Context, data any) error {
groupInfo, ok := data.(*sdkws.GroupInfo)
if !ok {
return errs.New("group info type error")
}
if groupInfo == nil {
return nil
}
local, err := g.db.GetJoinedGroupListDB(ctx)
if err != nil {
return err
}
log.ZDebug(ctx, "group info", "groupInfo", groupInfo)
changes := datautil.Batch(ServerGroupToLocalGroup, []*sdkws.GroupInfo{groupInfo})
kv := datautil.SliceToMapAny(local, func(e *model_struct.LocalGroup) (string, *model_struct.LocalGroup) {
return e.GroupID, e
})
for i, change := range changes {
key := change.GroupID
kv[key] = changes[i]
}
server := datautil.Values(kv)
return g.groupSyncer.Sync(ctx, server, local, nil)
},
Syncer: func(server, local []*model_struct.LocalGroupMember) error {
return g.groupMemberSyncer.Sync(ctx, server, local, nil)
},
FullSyncer: func(ctx context.Context) error {
return g.groupMemberSyncer.FullSync(ctx, groupID)
},
FullID: func(ctx context.Context) ([]string, error) {
resp, err := util.CallApi[group.GetFullGroupMemberUserIDsResp](ctx, constant.GetFullGroupMemberUserIDs, &group.GetFullGroupMemberUserIDsReq{
GroupID: groupID,
})
if err != nil {
return nil, err
}
return resp.UserIDs, nil
},
}
return groupMemberSyncer.CheckVersionSync()
}
func (g *Group) IncrSyncJoinGroup(ctx context.Context) error {
opt := incrversion.VersionSynchronizer[*model_struct.LocalGroup, *group.GetIncrementalJoinGroupResp]{
Ctx: ctx,
DB: g.db,
TableName: g.groupTableName(),
EntityID: g.loginUserID,
Key: func(LocalGroup *model_struct.LocalGroup) string {
return LocalGroup.GroupID
},
Local: func() ([]*model_struct.LocalGroup, error) {
return g.db.GetJoinedGroupListDB(ctx)
},
Server: func(version *model_struct.LocalVersionSync) (*group.GetIncrementalJoinGroupResp, error) {
return util.CallApi[group.GetIncrementalJoinGroupResp](ctx, constant.GetIncrementalJoinGroup, &group.GetIncrementalJoinGroupReq{
UserID: g.loginUserID,
Version: version.Version,
VersionID: version.VersionID,
})
},
Full: func(resp *group.GetIncrementalJoinGroupResp) bool {
return resp.Full
},
Version: func(resp *group.GetIncrementalJoinGroupResp) (string, uint64) {
return resp.VersionID, resp.Version
},
Delete: func(resp *group.GetIncrementalJoinGroupResp) []string {
return resp.Delete
},
Update: func(resp *group.GetIncrementalJoinGroupResp) []*model_struct.LocalGroup {
return datautil.Batch(ServerGroupToLocalGroup, resp.Update)
},
Insert: func(resp *group.GetIncrementalJoinGroupResp) []*model_struct.LocalGroup {
return datautil.Batch(ServerGroupToLocalGroup, resp.Insert)
},
Syncer: func(server, local []*model_struct.LocalGroup) error {
return g.groupSyncer.Sync(ctx, server, local, nil)
},
FullSyncer: func(ctx context.Context) error {
return g.groupSyncer.FullSync(ctx, g.loginUserID)
},
FullID: func(ctx context.Context) ([]string, error) {
resp, err := util.CallApi[group.GetFullJoinGroupIDsResp](ctx, constant.GetFullJoinedGroupIDs, &group.GetFullJoinGroupIDsReq{
UserID: g.loginUserID,
})
if err != nil {
return nil, err
}
return resp.GroupIDs, nil
},
}
return opt.Sync()
}

View File

@@ -0,0 +1 @@
package group