feat: incr sync version.
This commit is contained in:
97
go/chao-sdk-core/internal/group/conversion.go
Normal file
97
go/chao-sdk-core/internal/group/conversion.go
Normal 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),
|
||||
}
|
||||
}
|
||||
343
go/chao-sdk-core/internal/group/group.go
Normal file
343
go/chao-sdk-core/internal/group/group.go
Normal 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
|
||||
}
|
||||
245
go/chao-sdk-core/internal/group/notification.go
Normal file
245
go/chao-sdk-core/internal/group/notification.go
Normal 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()
|
||||
}
|
||||
}
|
||||
383
go/chao-sdk-core/internal/group/sdk.go
Normal file
383
go/chao-sdk-core/internal/group/sdk.go
Normal 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,
|
||||
}
|
||||
}
|
||||
310
go/chao-sdk-core/internal/group/sync.go
Normal file
310
go/chao-sdk-core/internal/group/sync.go
Normal 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
|
||||
}
|
||||
347
go/chao-sdk-core/internal/group/sync2.go
Normal file
347
go/chao-sdk-core/internal/group/sync2.go
Normal 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()
|
||||
}
|
||||
1
go/chao-sdk-core/internal/group/sync2_test.go
Normal file
1
go/chao-sdk-core/internal/group/sync2_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package group
|
||||
Reference in New Issue
Block a user