mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-04-28 14:29:19 +08:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
@@ -7,7 +21,7 @@ type CronTaskCmd struct {
|
||||
}
|
||||
|
||||
func NewCronTaskCmd() *CronTaskCmd {
|
||||
return &CronTaskCmd{NewRootCmd("cronTask")}
|
||||
return &CronTaskCmd{NewRootCmd("cronTask", WithCronTaskLogName())}
|
||||
}
|
||||
|
||||
func (c *CronTaskCmd) addRunE(f func() error) {
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
// Copyright © 2023 OpenIM. 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 cmd
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/msggateway"
|
||||
//"github.com/OpenIMSDK/Open-IM-Server/internal/msggateway"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
)
|
||||
|
||||
type MsgGatewayCmd struct {
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
// Copyright © 2023 OpenIM. 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 cmd
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/msgtransfer"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/msgtransfer"
|
||||
)
|
||||
|
||||
type MsgTransferCmd struct {
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
// Copyright © 2023 OpenIM. 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 cmd
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/tools"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/tools"
|
||||
)
|
||||
|
||||
type MsgUtilsCmd struct {
|
||||
|
||||
+45
-6
@@ -1,12 +1,27 @@
|
||||
// Copyright © 2023 OpenIM. 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type RootCmd struct {
|
||||
@@ -16,17 +31,40 @@ type RootCmd struct {
|
||||
prometheusPort int
|
||||
}
|
||||
|
||||
func NewRootCmd(name string) (rootCmd *RootCmd) {
|
||||
type CmdOpts struct {
|
||||
loggerPrefixName string
|
||||
}
|
||||
|
||||
func WithCronTaskLogName() func(*CmdOpts) {
|
||||
return func(opts *CmdOpts) {
|
||||
opts.loggerPrefixName = "OpenIM.CronTask.log.all"
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogName(logName string) func(*CmdOpts) {
|
||||
return func(opts *CmdOpts) {
|
||||
opts.loggerPrefixName = logName
|
||||
}
|
||||
}
|
||||
|
||||
func NewRootCmd(name string, opts ...func(*CmdOpts)) (rootCmd *RootCmd) {
|
||||
rootCmd = &RootCmd{Name: name}
|
||||
c := cobra.Command{
|
||||
Use: "start",
|
||||
Short: fmt.Sprintf(`Start %s server`, name),
|
||||
Long: fmt.Sprintf(`Start %s server`, name),
|
||||
Use: "start openIM application",
|
||||
Short: fmt.Sprintf(`Start %s `, name),
|
||||
Long: fmt.Sprintf(`Start %s `, name),
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := rootCmd.getConfFromCmdAndInit(cmd); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := log.InitFromConfig("OpenIM.log.all", name, config.Config.Log.RemainLogLevel, config.Config.Log.IsStdout, config.Config.Log.IsJson, config.Config.Log.StorageLocation, config.Config.Log.RemainRotationCount); err != nil {
|
||||
cmdOpts := &CmdOpts{}
|
||||
for _, opt := range opts {
|
||||
opt(cmdOpts)
|
||||
}
|
||||
if cmdOpts.loggerPrefixName == "" {
|
||||
cmdOpts.loggerPrefixName = "OpenIM.log.all"
|
||||
}
|
||||
if err := log.InitFromConfig(cmdOpts.loggerPrefixName, name, config.Config.Log.RemainLogLevel, config.Config.Log.IsStdout, config.Config.Log.IsJson, config.Config.Log.StorageLocation, config.Config.Log.RemainRotationCount); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return nil
|
||||
@@ -69,6 +107,7 @@ func (r *RootCmd) GetPrometheusPortFlag() int {
|
||||
|
||||
func (r *RootCmd) getConfFromCmdAndInit(cmdLines *cobra.Command) error {
|
||||
configFolderPath, _ := cmdLines.Flags().GetString(constant.FlagConf)
|
||||
fmt.Println("configFolderPath:", configFolderPath)
|
||||
return config.InitConfig(configFolderPath)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func (a *RpcCmd) Exec() error {
|
||||
return a.Execute()
|
||||
}
|
||||
|
||||
func (a *RpcCmd) StartSvr(name string, rpcFn func(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error) error {
|
||||
func (a *RpcCmd) StartSvr(name string, rpcFn func(discov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error) error {
|
||||
if a.GetPortFlag() == 0 {
|
||||
return errors.New("port is required")
|
||||
}
|
||||
|
||||
+47
-43
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 config
|
||||
|
||||
import (
|
||||
@@ -77,15 +91,11 @@ type config struct {
|
||||
MsgToPush struct {
|
||||
Topic string `yaml:"topic"`
|
||||
} `yaml:"msgToPush"`
|
||||
MsgToModify struct {
|
||||
Topic string `yaml:"topic"`
|
||||
} `yaml:"msgToModify"`
|
||||
ConsumerGroupID struct {
|
||||
MsgToRedis string `yaml:"msgToRedis"`
|
||||
MsgToMongo string `yaml:"msgToMongo"`
|
||||
MsgToMySql string `yaml:"msgToMySql"`
|
||||
MsgToPush string `yaml:"msgToPush"`
|
||||
MsgToModify string `yaml:"msgToModify"`
|
||||
MsgToRedis string `yaml:"msgToRedis"`
|
||||
MsgToMongo string `yaml:"msgToMongo"`
|
||||
MsgToMySql string `yaml:"msgToMySql"`
|
||||
MsgToPush string `yaml:"msgToPush"`
|
||||
} `yaml:"consumerGroupID"`
|
||||
} `yaml:"kafka"`
|
||||
|
||||
@@ -103,42 +113,26 @@ type config struct {
|
||||
Enable string `yaml:"enable"`
|
||||
ApiURL string `yaml:"apiURL"`
|
||||
Minio struct {
|
||||
TempBucket string `yaml:"tempBucket"`
|
||||
DataBucket string `yaml:"dataBucket"`
|
||||
Location string `yaml:"location"`
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
AccessKeyID string `yaml:"accessKeyID"`
|
||||
SecretAccessKey string `yaml:"secretAccessKey"`
|
||||
IsDistributedMod bool `yaml:"isDistributedMod"`
|
||||
Bucket string `yaml:"bucket"`
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
AccessKeyID string `yaml:"accessKeyID"`
|
||||
SecretAccessKey string `yaml:"secretAccessKey"`
|
||||
SessionToken string `yaml:"sessionToken"`
|
||||
} `yaml:"minio"`
|
||||
Tencent struct {
|
||||
AppID string `yaml:"appID"`
|
||||
Region string `yaml:"region"`
|
||||
Bucket string `yaml:"bucket"`
|
||||
SecretID string `yaml:"secretID"`
|
||||
SecretKey string `yaml:"secretKey"`
|
||||
} `yaml:"tencent"`
|
||||
Ali struct {
|
||||
RegionID string `yaml:"regionID"`
|
||||
AccessKeyID string `yaml:"accessKeyID"`
|
||||
AccessKeySecret string `yaml:"accessKeySecret"`
|
||||
StsEndpoint string `yaml:"stsEndpoint"`
|
||||
OssEndpoint string `yaml:"ossEndpoint"`
|
||||
Bucket string `yaml:"bucket"`
|
||||
FinalHost string `yaml:"finalHost"`
|
||||
StsDurationSeconds int64 `yaml:"stsDurationSeconds"`
|
||||
OssRoleArn string `yaml:"OssRoleArn"`
|
||||
} `yaml:"ali"`
|
||||
Aws struct {
|
||||
Cos struct {
|
||||
BucketURL string `yaml:"bucketURL"`
|
||||
SecretID string `yaml:"secretID"`
|
||||
SecretKey string `yaml:"secretKey"`
|
||||
SessionToken string `yaml:"sessionToken"`
|
||||
} `yaml:"cos"`
|
||||
Oss struct {
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
Bucket string `yaml:"bucket"`
|
||||
BucketURL string `yaml:"bucketURL"`
|
||||
AccessKeyID string `yaml:"accessKeyID"`
|
||||
AccessKeySecret string `yaml:"accessKeySecret"`
|
||||
Region string `yaml:"region"`
|
||||
Bucket string `yaml:"bucket"`
|
||||
FinalHost string `yaml:"finalHost"`
|
||||
RoleArn string `yaml:"roleArn"`
|
||||
ExternalId string `yaml:"externalId"`
|
||||
RoleSessionName string `yaml:"roleSessionName"`
|
||||
} `yaml:"aws"`
|
||||
SessionToken string `yaml:"sessionToken"`
|
||||
} `yaml:"oss"`
|
||||
} `yaml:"object"`
|
||||
|
||||
RpcPort struct {
|
||||
@@ -215,6 +209,7 @@ type config struct {
|
||||
SingleMessageHasReadReceiptEnable bool `yaml:"singleMessageHasReadReceiptEnable"`
|
||||
RetainChatRecords int `yaml:"retainChatRecords"`
|
||||
ChatRecordsClearTime string `yaml:"chatRecordsClearTime"`
|
||||
MsgDestructTime string `yaml:"msgDestructTime"`
|
||||
Secret string `yaml:"secret"`
|
||||
TokenPolicy struct {
|
||||
Expire int64 `yaml:"expire"`
|
||||
@@ -303,6 +298,15 @@ type notification struct {
|
||||
}
|
||||
|
||||
func GetServiceNames() []string {
|
||||
return []string{Config.RpcRegisterName.OpenImUserName, Config.RpcRegisterName.OpenImFriendName, Config.RpcRegisterName.OpenImMsgName, Config.RpcRegisterName.OpenImPushName, Config.RpcRegisterName.OpenImMessageGatewayName,
|
||||
Config.RpcRegisterName.OpenImGroupName, Config.RpcRegisterName.OpenImAuthName, Config.RpcRegisterName.OpenImConversationName, Config.RpcRegisterName.OpenImThirdName}
|
||||
return []string{
|
||||
Config.RpcRegisterName.OpenImUserName,
|
||||
Config.RpcRegisterName.OpenImFriendName,
|
||||
Config.RpcRegisterName.OpenImMsgName,
|
||||
Config.RpcRegisterName.OpenImPushName,
|
||||
Config.RpcRegisterName.OpenImMessageGatewayName,
|
||||
Config.RpcRegisterName.OpenImGroupName,
|
||||
Config.RpcRegisterName.OpenImAuthName,
|
||||
Config.RpcRegisterName.OpenImConversationName,
|
||||
Config.RpcRegisterName.OpenImThirdName,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
// Copyright © 2023 OpenIM. 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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 constant
|
||||
|
||||
const (
|
||||
@@ -126,7 +140,8 @@ const (
|
||||
SingleTerminalLogin = 2
|
||||
//The web side can be online at the same time, and the other side can only log in at one end
|
||||
WebAndOther = 3
|
||||
//The PC side is mutually exclusive, and the mobile side is mutually exclusive, but the web side can be online at the same time
|
||||
// The PC side is mutually exclusive, and the mobile side is mutually exclusive, but the web side can be online at
|
||||
// the same time
|
||||
PcMobileAndWeb = 4
|
||||
//The PC terminal can be online at the same time,but other terminal only one of the endpoints can login
|
||||
PCAndOther = 5
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 constant
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 constant
|
||||
|
||||
// fixme 1<--->IOS 2<--->Android 3<--->Windows
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 convert
|
||||
|
||||
import (
|
||||
@@ -8,7 +22,11 @@ import (
|
||||
sdk "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
)
|
||||
|
||||
func BlackDB2Pb(ctx context.Context, blackDBs []*relation.BlackModel, f func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (blackPbs []*sdk.BlackInfo, err error) {
|
||||
func BlackDB2Pb(
|
||||
ctx context.Context,
|
||||
blackDBs []*relation.BlackModel,
|
||||
f func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error),
|
||||
) (blackPbs []*sdk.BlackInfo, err error) {
|
||||
var userIDs []string
|
||||
for _, blackDB := range blackDBs {
|
||||
userIDs = append(userIDs, blackDB.BlockUserID)
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 convert
|
||||
|
||||
import (
|
||||
@@ -8,6 +22,7 @@ import (
|
||||
|
||||
func ConversationDB2Pb(conversationDB *relation.ConversationModel) *conversation.Conversation {
|
||||
conversationPB := &conversation.Conversation{}
|
||||
conversationPB.LatestMsgDestructTime = conversationDB.LatestMsgDestructTime.Unix()
|
||||
if err := utils.CopyStructFields(conversationPB, conversationDB); err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -20,6 +35,7 @@ func ConversationsDB2Pb(conversationsDB []*relation.ConversationModel) (conversa
|
||||
if err := utils.CopyStructFields(conversationPB, conversationDB); err != nil {
|
||||
continue
|
||||
}
|
||||
conversationPB.LatestMsgDestructTime = conversationDB.LatestMsgDestructTime.Unix()
|
||||
conversationsPB = append(conversationsPB, conversationPB)
|
||||
}
|
||||
return conversationsPB
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 convert
|
||||
|
||||
import (
|
||||
@@ -16,7 +30,11 @@ func FriendPb2DB(friend *sdkws.FriendInfo) *relation.FriendModel {
|
||||
return dbFriend
|
||||
}
|
||||
|
||||
func FriendDB2Pb(ctx context.Context, friendDB *relation.FriendModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (*sdkws.FriendInfo, error) {
|
||||
func FriendDB2Pb(
|
||||
ctx context.Context,
|
||||
friendDB *relation.FriendModel,
|
||||
getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error),
|
||||
) (*sdkws.FriendInfo, error) {
|
||||
pbfriend := &sdkws.FriendInfo{FriendUser: &sdkws.UserInfo{}}
|
||||
utils.CopyStructFields(pbfriend, friendDB)
|
||||
users, err := getUsers(ctx, []string{friendDB.FriendUserID})
|
||||
@@ -31,7 +49,11 @@ func FriendDB2Pb(ctx context.Context, friendDB *relation.FriendModel, getUsers f
|
||||
return pbfriend, nil
|
||||
}
|
||||
|
||||
func FriendsDB2Pb(ctx context.Context, friendsDB []*relation.FriendModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (friendsPb []*sdkws.FriendInfo, err error) {
|
||||
func FriendsDB2Pb(
|
||||
ctx context.Context,
|
||||
friendsDB []*relation.FriendModel,
|
||||
getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error),
|
||||
) (friendsPb []*sdkws.FriendInfo, err error) {
|
||||
var userID []string
|
||||
for _, friendDB := range friendsDB {
|
||||
userID = append(userID, friendDB.FriendUserID)
|
||||
@@ -53,7 +75,11 @@ func FriendsDB2Pb(ctx context.Context, friendsDB []*relation.FriendModel, getUse
|
||||
return friendsPb, nil
|
||||
}
|
||||
|
||||
func FriendRequestDB2Pb(ctx context.Context, friendRequests []*relation.FriendRequestModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) ([]*sdkws.FriendRequest, error) {
|
||||
func FriendRequestDB2Pb(
|
||||
ctx context.Context,
|
||||
friendRequests []*relation.FriendRequestModel,
|
||||
getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error),
|
||||
) ([]*sdkws.FriendRequest, error) {
|
||||
userIDMap := make(map[string]struct{})
|
||||
for _, friendRequest := range friendRequests {
|
||||
userIDMap[friendRequest.ToUserID] = struct{}{}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 convert
|
||||
|
||||
import (
|
||||
@@ -41,7 +55,12 @@ func Pb2DbGroupRequest(req *pbGroup.GroupApplicationResponseReq, handleUserID st
|
||||
}
|
||||
}
|
||||
|
||||
func Db2PbCMSGroup(m *relation.GroupModel, ownerUserID string, ownerUserName string, memberCount uint32) *pbGroup.CMSGroup {
|
||||
func Db2PbCMSGroup(
|
||||
m *relation.GroupModel,
|
||||
ownerUserID string,
|
||||
ownerUserName string,
|
||||
memberCount uint32,
|
||||
) *pbGroup.CMSGroup {
|
||||
return &pbGroup.CMSGroup{
|
||||
GroupInfo: Db2PbGroupInfo(m, ownerUserID, memberCount),
|
||||
GroupOwnerUserID: ownerUserID,
|
||||
@@ -66,7 +85,11 @@ func Db2PbGroupMember(m *relation.GroupMemberModel) *sdkws.GroupMemberFullInfo {
|
||||
}
|
||||
}
|
||||
|
||||
func Db2PbGroupRequest(m *relation.GroupRequestModel, user *sdkws.PublicUserInfo, group *sdkws.GroupInfo) *sdkws.GroupRequest {
|
||||
func Db2PbGroupRequest(
|
||||
m *relation.GroupRequestModel,
|
||||
user *sdkws.PublicUserInfo,
|
||||
group *sdkws.GroupInfo,
|
||||
) *sdkws.GroupRequest {
|
||||
return &sdkws.GroupRequest{
|
||||
UserInfo: user,
|
||||
GroupInfo: group,
|
||||
@@ -82,7 +105,11 @@ func Db2PbGroupRequest(m *relation.GroupRequestModel, user *sdkws.PublicUserInfo
|
||||
}
|
||||
}
|
||||
|
||||
func Db2PbGroupAbstractInfo(groupID string, groupMemberNumber uint32, groupMemberListHash uint64) *pbGroup.GroupAbstractInfo {
|
||||
func Db2PbGroupAbstractInfo(
|
||||
groupID string,
|
||||
groupMemberNumber uint32,
|
||||
groupMemberListHash uint64,
|
||||
) *pbGroup.GroupAbstractInfo {
|
||||
return &pbGroup.GroupAbstractInfo{
|
||||
GroupID: groupID,
|
||||
GroupMemberNumber: groupMemberNumber,
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 convert
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 convert
|
||||
|
||||
import (
|
||||
|
||||
Vendored
+30
-5
@@ -1,12 +1,27 @@
|
||||
// Copyright © 2023 OpenIM. 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 cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -31,7 +46,11 @@ type BlackCacheRedis struct {
|
||||
blackDB relationTb.BlackModelInterface
|
||||
}
|
||||
|
||||
func NewBlackCacheRedis(rdb redis.UniversalClient, blackDB relationTb.BlackModelInterface, options rockscache.Options) BlackCache {
|
||||
func NewBlackCacheRedis(
|
||||
rdb redis.UniversalClient,
|
||||
blackDB relationTb.BlackModelInterface,
|
||||
options rockscache.Options,
|
||||
) BlackCache {
|
||||
rcClient := rockscache.NewClient(rdb, options)
|
||||
return &BlackCacheRedis{
|
||||
expireTime: blackExpireTime,
|
||||
@@ -55,9 +74,15 @@ func (b *BlackCacheRedis) getBlackIDsKey(ownerUserID string) string {
|
||||
}
|
||||
|
||||
func (b *BlackCacheRedis) GetBlackIDs(ctx context.Context, userID string) (blackIDs []string, err error) {
|
||||
return getCache(ctx, b.rcClient, b.getBlackIDsKey(userID), b.expireTime, func(ctx context.Context) ([]string, error) {
|
||||
return b.blackDB.FindBlackUserIDs(ctx, userID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
b.rcClient,
|
||||
b.getBlackIDsKey(userID),
|
||||
b.expireTime,
|
||||
func(ctx context.Context) ([]string, error) {
|
||||
return b.blackDB.FindBlackUserIDs(ctx, userID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (b *BlackCacheRedis) DelBlackIDs(ctx context.Context, userID string) BlackCache {
|
||||
|
||||
Vendored
+202
-62
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 cache
|
||||
|
||||
import (
|
||||
@@ -7,11 +21,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/relation"
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -42,7 +57,11 @@ type ConversationCache interface {
|
||||
DelConvsersations(ownerUserID string, conversationIDs ...string) ConversationCache
|
||||
DelUsersConversation(conversationID string, ownerUserIDs ...string) ConversationCache
|
||||
// get one conversation from msgCache
|
||||
GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error)
|
||||
GetConversations(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
conversationIDs []string,
|
||||
) ([]*relationTb.ConversationModel, error)
|
||||
// get one user's all conversations from msgCache
|
||||
GetUserAllConversations(ctx context.Context, ownerUserID string) ([]*relationTb.ConversationModel, error)
|
||||
// get user conversation recv msg from msgCache
|
||||
@@ -58,13 +77,25 @@ type ConversationCache interface {
|
||||
GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error)
|
||||
DelUserAllHasReadSeqs(ownerUserID string, conversationIDs ...string) ConversationCache
|
||||
|
||||
GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error)
|
||||
GetConversationsByConversationID(
|
||||
ctx context.Context,
|
||||
conversationIDs []string,
|
||||
) ([]*relationTb.ConversationModel, error)
|
||||
DelConversationByConversationID(conversationIDs ...string) ConversationCache
|
||||
}
|
||||
|
||||
func NewConversationRedis(rdb redis.UniversalClient, opts rockscache.Options, db relationTb.ConversationModelInterface) ConversationCache {
|
||||
func NewConversationRedis(
|
||||
rdb redis.UniversalClient,
|
||||
opts rockscache.Options,
|
||||
db relationTb.ConversationModelInterface,
|
||||
) ConversationCache {
|
||||
rcClient := rockscache.NewClient(rdb, opts)
|
||||
return &ConversationRedisCache{rcClient: rcClient, metaCache: NewMetaCacheRedis(rcClient), conversationDB: db, expireTime: conversationExpireTime}
|
||||
return &ConversationRedisCache{
|
||||
rcClient: rcClient,
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
conversationDB: db,
|
||||
expireTime: conversationExpireTime,
|
||||
}
|
||||
}
|
||||
|
||||
type ConversationRedisCache struct {
|
||||
@@ -74,13 +105,27 @@ type ConversationRedisCache struct {
|
||||
expireTime time.Duration
|
||||
}
|
||||
|
||||
func NewNewConversationRedis(rdb redis.UniversalClient, conversationDB *relation.ConversationGorm, options rockscache.Options) ConversationCache {
|
||||
func NewNewConversationRedis(
|
||||
rdb redis.UniversalClient,
|
||||
conversationDB *relation.ConversationGorm,
|
||||
options rockscache.Options,
|
||||
) ConversationCache {
|
||||
rcClient := rockscache.NewClient(rdb, options)
|
||||
return &ConversationRedisCache{rcClient: rcClient, metaCache: NewMetaCacheRedis(rcClient), conversationDB: conversationDB, expireTime: conversationExpireTime}
|
||||
return &ConversationRedisCache{
|
||||
rcClient: rcClient,
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
conversationDB: conversationDB,
|
||||
expireTime: conversationExpireTime,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) NewCache() ConversationCache {
|
||||
return &ConversationRedisCache{rcClient: c.rcClient, metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...), conversationDB: c.conversationDB, expireTime: c.expireTime}
|
||||
return &ConversationRedisCache{
|
||||
rcClient: c.rcClient,
|
||||
metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...),
|
||||
conversationDB: c.conversationDB,
|
||||
expireTime: c.expireTime,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getConversationKey(ownerUserID, conversationID string) string {
|
||||
@@ -108,9 +153,15 @@ func (c *ConversationRedisCache) getConversationHasReadSeqKey(ownerUserID, conve
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetUserConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) {
|
||||
return getCache(ctx, c.rcClient, c.getConversationIDsKey(ownerUserID), c.expireTime, func(ctx context.Context) ([]string, error) {
|
||||
return c.conversationDB.FindUserIDAllConversationID(ctx, ownerUserID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
c.getConversationIDsKey(ownerUserID),
|
||||
c.expireTime,
|
||||
func(ctx context.Context) ([]string, error) {
|
||||
return c.conversationDB.FindUserIDAllConversationID(ctx, ownerUserID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) DelConversationIDs(userIDs ...string) ConversationCache {
|
||||
@@ -127,17 +178,26 @@ func (c *ConversationRedisCache) getUserConversationIDsHashKey(ownerUserID strin
|
||||
return conversationIDsHashKey + ownerUserID
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) {
|
||||
return getCache(ctx, c.rcClient, c.getUserConversationIDsHashKey(ownerUserID), c.expireTime, func(ctx context.Context) (uint64, error) {
|
||||
conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
utils.Sort(conversationIDs, true)
|
||||
bi := big.NewInt(0)
|
||||
bi.SetString(utils.Md5(strings.Join(conversationIDs, ";"))[0:8], 16)
|
||||
return bi.Uint64(), nil
|
||||
})
|
||||
func (c *ConversationRedisCache) GetUserConversationIDsHash(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
) (hash uint64, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
c.getUserConversationIDsHashKey(ownerUserID),
|
||||
c.expireTime,
|
||||
func(ctx context.Context) (uint64, error) {
|
||||
conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
utils.Sort(conversationIDs, true)
|
||||
bi := big.NewInt(0)
|
||||
bi.SetString(utils.Md5(strings.Join(conversationIDs, ";"))[0:8], 16)
|
||||
return bi.Uint64(), nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) DelUserConversationIDsHash(ownerUserIDs ...string) ConversationCache {
|
||||
@@ -150,10 +210,19 @@ func (c *ConversationRedisCache) DelUserConversationIDsHash(ownerUserIDs ...stri
|
||||
return cache
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetConversation(ctx context.Context, ownerUserID, conversationID string) (*relationTb.ConversationModel, error) {
|
||||
return getCache(ctx, c.rcClient, c.getConversationKey(ownerUserID, conversationID), c.expireTime, func(ctx context.Context) (*relationTb.ConversationModel, error) {
|
||||
return c.conversationDB.Take(ctx, ownerUserID, conversationID)
|
||||
})
|
||||
func (c *ConversationRedisCache) GetConversation(
|
||||
ctx context.Context,
|
||||
ownerUserID, conversationID string,
|
||||
) (*relationTb.ConversationModel, error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
c.getConversationKey(ownerUserID, conversationID),
|
||||
c.expireTime,
|
||||
func(ctx context.Context) (*relationTb.ConversationModel, error) {
|
||||
return c.conversationDB.Take(ctx, ownerUserID, conversationID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) DelConvsersations(ownerUserID string, convsersationIDs ...string) ConversationCache {
|
||||
@@ -166,7 +235,10 @@ func (c *ConversationRedisCache) DelConvsersations(ownerUserID string, convsersa
|
||||
return cache
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getConversationIndex(convsation *relationTb.ConversationModel, keys []string) (int, error) {
|
||||
func (c *ConversationRedisCache) getConversationIndex(
|
||||
convsation *relationTb.ConversationModel,
|
||||
keys []string,
|
||||
) (int, error) {
|
||||
key := c.getConversationKey(convsation.OwnerUserID, convsation.ConversationID)
|
||||
for _i, _key := range keys {
|
||||
if _key == key {
|
||||
@@ -176,17 +248,31 @@ func (c *ConversationRedisCache) getConversationIndex(convsation *relationTb.Con
|
||||
return 0, errors.New("not found key:" + key + " in keys")
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error) {
|
||||
func (c *ConversationRedisCache) GetConversations(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
conversationIDs []string,
|
||||
) ([]*relationTb.ConversationModel, error) {
|
||||
var keys []string
|
||||
for _, conversarionID := range conversationIDs {
|
||||
keys = append(keys, c.getConversationKey(ownerUserID, conversarionID))
|
||||
}
|
||||
return batchGetCache(ctx, c.rcClient, keys, c.expireTime, c.getConversationIndex, func(ctx context.Context) ([]*relationTb.ConversationModel, error) {
|
||||
return c.conversationDB.Find(ctx, ownerUserID, conversationIDs)
|
||||
})
|
||||
return batchGetCache(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
keys,
|
||||
c.expireTime,
|
||||
c.getConversationIndex,
|
||||
func(ctx context.Context) ([]*relationTb.ConversationModel, error) {
|
||||
return c.conversationDB.Find(ctx, ownerUserID, conversationIDs)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetUserAllConversations(ctx context.Context, ownerUserID string) ([]*relationTb.ConversationModel, error) {
|
||||
func (c *ConversationRedisCache) GetUserAllConversations(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
) ([]*relationTb.ConversationModel, error) {
|
||||
conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -195,21 +281,46 @@ func (c *ConversationRedisCache) GetUserAllConversations(ctx context.Context, ow
|
||||
for _, conversarionID := range conversationIDs {
|
||||
keys = append(keys, c.getConversationKey(ownerUserID, conversarionID))
|
||||
}
|
||||
return batchGetCache(ctx, c.rcClient, keys, c.expireTime, c.getConversationIndex, func(ctx context.Context) ([]*relationTb.ConversationModel, error) {
|
||||
return c.conversationDB.FindUserIDAllConversations(ctx, ownerUserID)
|
||||
})
|
||||
return batchGetCache(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
keys,
|
||||
c.expireTime,
|
||||
c.getConversationIndex,
|
||||
func(ctx context.Context) ([]*relationTb.ConversationModel, error) {
|
||||
return c.conversationDB.FindUserIDAllConversations(ctx, ownerUserID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) {
|
||||
return getCache(ctx, c.rcClient, c.getRecvMsgOptKey(ownerUserID, conversationID), c.expireTime, func(ctx context.Context) (opt int, err error) {
|
||||
return c.conversationDB.GetUserRecvMsgOpt(ctx, ownerUserID, conversationID)
|
||||
})
|
||||
func (c *ConversationRedisCache) GetUserRecvMsgOpt(
|
||||
ctx context.Context,
|
||||
ownerUserID, conversationID string,
|
||||
) (opt int, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
c.getRecvMsgOptKey(ownerUserID, conversationID),
|
||||
c.expireTime,
|
||||
func(ctx context.Context) (opt int, err error) {
|
||||
return c.conversationDB.GetUserRecvMsgOpt(ctx, ownerUserID, conversationID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error) {
|
||||
return getCache(ctx, c.rcClient, c.getSuperGroupRecvNotNotifyUserIDsKey(groupID), c.expireTime, func(ctx context.Context) (userIDs []string, err error) {
|
||||
return c.conversationDB.FindSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID)
|
||||
})
|
||||
func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDs(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
) (userIDs []string, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
c.getSuperGroupRecvNotNotifyUserIDsKey(groupID),
|
||||
c.expireTime,
|
||||
func(ctx context.Context) (userIDs []string, err error) {
|
||||
return c.conversationDB.FindSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) DelUsersConversation(conversationID string, ownerUserIDs ...string) ConversationCache {
|
||||
@@ -234,17 +345,26 @@ func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDs(groupID st
|
||||
return cache
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDsHash(ctx context.Context, groupID string) (hash uint64, err error) {
|
||||
return getCache(ctx, c.rcClient, c.getSuperGroupRecvNotNotifyUserIDsHashKey(groupID), c.expireTime, func(ctx context.Context) (hash uint64, err error) {
|
||||
userIDs, err := c.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
utils.Sort(userIDs, true)
|
||||
bi := big.NewInt(0)
|
||||
bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16)
|
||||
return bi.Uint64(), nil
|
||||
})
|
||||
func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDsHash(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
) (hash uint64, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
c.getSuperGroupRecvNotNotifyUserIDsHashKey(groupID),
|
||||
c.expireTime,
|
||||
func(ctx context.Context) (hash uint64, err error) {
|
||||
userIDs, err := c.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
utils.Sort(userIDs, true)
|
||||
bi := big.NewInt(0)
|
||||
bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16)
|
||||
return bi.Uint64(), nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDsHash(groupID string) ConversationCache {
|
||||
@@ -253,7 +373,10 @@ func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDsHash(groupI
|
||||
return cache
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(conversationID string, conversationIDs []string) (int, error) {
|
||||
func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(
|
||||
conversationID string,
|
||||
conversationIDs []string,
|
||||
) (int, error) {
|
||||
for _i, _conversationID := range conversationIDs {
|
||||
if _conversationID == conversationID {
|
||||
return _i, nil
|
||||
@@ -262,7 +385,10 @@ func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(conversationID strin
|
||||
return 0, errors.New("not found key:" + conversationID + " in keys")
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) {
|
||||
func (c *ConversationRedisCache) GetUserAllHasReadSeqs(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
) (map[string]int64, error) {
|
||||
conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -271,12 +397,23 @@ func (c *ConversationRedisCache) GetUserAllHasReadSeqs(ctx context.Context, owne
|
||||
for _, conversarionID := range conversationIDs {
|
||||
keys = append(keys, c.getConversationHasReadSeqKey(ownerUserID, conversarionID))
|
||||
}
|
||||
return batchGetCacheMap(ctx, c.rcClient, keys, conversationIDs, c.expireTime, c.getUserAllHasReadSeqsIndex, func(ctx context.Context) (map[string]int64, error) {
|
||||
return c.conversationDB.GetUserAllHasReadSeqs(ctx, ownerUserID)
|
||||
})
|
||||
return batchGetCacheMap(
|
||||
ctx,
|
||||
c.rcClient,
|
||||
keys,
|
||||
conversationIDs,
|
||||
c.expireTime,
|
||||
c.getUserAllHasReadSeqsIndex,
|
||||
func(ctx context.Context) (map[string]int64, error) {
|
||||
return c.conversationDB.GetUserAllHasReadSeqs(ctx, ownerUserID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) DelUserAllHasReadSeqs(ownerUserID string, conversationIDs ...string) ConversationCache {
|
||||
func (c *ConversationRedisCache) DelUserAllHasReadSeqs(
|
||||
ownerUserID string,
|
||||
conversationIDs ...string,
|
||||
) ConversationCache {
|
||||
cache := c.NewCache()
|
||||
for _, conversationID := range conversationIDs {
|
||||
cache.AddKeys(c.getConversationHasReadSeqKey(ownerUserID, conversationID))
|
||||
@@ -284,7 +421,10 @@ func (c *ConversationRedisCache) DelUserAllHasReadSeqs(ownerUserID string, conve
|
||||
return cache
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error) {
|
||||
func (c *ConversationRedisCache) GetConversationsByConversationID(
|
||||
ctx context.Context,
|
||||
conversationIDs []string,
|
||||
) ([]*relationTb.ConversationModel, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
const (
|
||||
extendMsgSetCache = "EXTEND_MSG_SET_CACHE:"
|
||||
extendMsgCache = "EXTEND_MSG_CACHE:"
|
||||
)
|
||||
|
||||
type ExtendMsgSetCache interface {
|
||||
metaCache
|
||||
NewCache() ExtendMsgSetCache
|
||||
GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, firstModifyTime int64) (extendMsg *unrelation.ExtendMsgModel, err error)
|
||||
DelExtendMsg(clientMsgID string) ExtendMsgSetCache
|
||||
}
|
||||
|
||||
type ExtendMsgSetCacheRedis struct {
|
||||
metaCache
|
||||
expireTime time.Duration
|
||||
rcClient *rockscache.Client
|
||||
extendMsgSetDB unrelation.ExtendMsgSetModelInterface
|
||||
}
|
||||
|
||||
func NewExtendMsgSetCacheRedis(rdb redis.UniversalClient, extendMsgSetDB unrelation.ExtendMsgSetModelInterface, options rockscache.Options) ExtendMsgSetCache {
|
||||
rcClient := rockscache.NewClient(rdb, options)
|
||||
return &ExtendMsgSetCacheRedis{
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
expireTime: time.Second * 30 * 60,
|
||||
extendMsgSetDB: extendMsgSetDB,
|
||||
rcClient: rcClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetCacheRedis) NewCache() ExtendMsgSetCache {
|
||||
return &ExtendMsgSetCacheRedis{
|
||||
metaCache: NewMetaCacheRedis(e.rcClient, e.metaCache.GetPreDelKeys()...),
|
||||
expireTime: e.expireTime,
|
||||
extendMsgSetDB: e.extendMsgSetDB,
|
||||
rcClient: e.rcClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetCacheRedis) getKey(clientMsgID string) string {
|
||||
return extendMsgCache + clientMsgID
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetCacheRedis) GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, firstModifyTime int64) (extendMsg *unrelation.ExtendMsgModel, err error) {
|
||||
return getCache(ctx, e.rcClient, e.getKey(clientMsgID), e.expireTime, func(ctx context.Context) (*unrelation.ExtendMsgModel, error) {
|
||||
return e.extendMsgSetDB.TakeExtendMsg(ctx, conversationID, sessionType, clientMsgID, firstModifyTime)
|
||||
})
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetCacheRedis) DelExtendMsg(clientMsgID string) ExtendMsgSetCache {
|
||||
new := e.NewCache()
|
||||
new.AddKeys(e.getKey(clientMsgID))
|
||||
return new
|
||||
}
|
||||
Vendored
+54
-12
@@ -1,13 +1,28 @@
|
||||
// Copyright © 2023 OpenIM. 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 cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -37,7 +52,11 @@ type FriendCacheRedis struct {
|
||||
rcClient *rockscache.Client
|
||||
}
|
||||
|
||||
func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationTb.FriendModelInterface, options rockscache.Options) FriendCache {
|
||||
func NewFriendCacheRedis(
|
||||
rdb redis.UniversalClient,
|
||||
friendDB relationTb.FriendModelInterface,
|
||||
options rockscache.Options,
|
||||
) FriendCache {
|
||||
rcClient := rockscache.NewClient(rdb, options)
|
||||
return &FriendCacheRedis{
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
@@ -48,7 +67,12 @@ func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationTb.FriendMo
|
||||
}
|
||||
|
||||
func (c *FriendCacheRedis) NewCache() FriendCache {
|
||||
return &FriendCacheRedis{rcClient: c.rcClient, metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...), friendDB: c.friendDB, expireTime: c.expireTime}
|
||||
return &FriendCacheRedis{
|
||||
rcClient: c.rcClient,
|
||||
metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...),
|
||||
friendDB: c.friendDB,
|
||||
expireTime: c.expireTime,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FriendCacheRedis) getFriendIDsKey(ownerUserID string) string {
|
||||
@@ -64,9 +88,15 @@ func (f *FriendCacheRedis) getFriendKey(ownerUserID, friendUserID string) string
|
||||
}
|
||||
|
||||
func (f *FriendCacheRedis) GetFriendIDs(ctx context.Context, ownerUserID string) (friendIDs []string, err error) {
|
||||
return getCache(ctx, f.rcClient, f.getFriendIDsKey(ownerUserID), f.expireTime, func(ctx context.Context) ([]string, error) {
|
||||
return f.friendDB.FindFriendUserIDs(ctx, ownerUserID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
f.rcClient,
|
||||
f.getFriendIDsKey(ownerUserID),
|
||||
f.expireTime,
|
||||
func(ctx context.Context) ([]string, error) {
|
||||
return f.friendDB.FindFriendUserIDs(ctx, ownerUserID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (f *FriendCacheRedis) DelFriendIDs(ownerUserID ...string) FriendCache {
|
||||
@@ -80,7 +110,10 @@ func (f *FriendCacheRedis) DelFriendIDs(ownerUserID ...string) FriendCache {
|
||||
}
|
||||
|
||||
// todo
|
||||
func (f *FriendCacheRedis) GetTwoWayFriendIDs(ctx context.Context, ownerUserID string) (twoWayFriendIDs []string, err error) {
|
||||
func (f *FriendCacheRedis) GetTwoWayFriendIDs(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
) (twoWayFriendIDs []string, err error) {
|
||||
friendIDs, err := f.GetFriendIDs(ctx, ownerUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -103,10 +136,19 @@ func (f *FriendCacheRedis) DelTwoWayFriendIDs(ctx context.Context, ownerUserID s
|
||||
return new
|
||||
}
|
||||
|
||||
func (f *FriendCacheRedis) GetFriend(ctx context.Context, ownerUserID, friendUserID string) (friend *relationTb.FriendModel, err error) {
|
||||
return getCache(ctx, f.rcClient, f.getFriendKey(ownerUserID, friendUserID), f.expireTime, func(ctx context.Context) (*relationTb.FriendModel, error) {
|
||||
return f.friendDB.Take(ctx, ownerUserID, friendUserID)
|
||||
})
|
||||
func (f *FriendCacheRedis) GetFriend(
|
||||
ctx context.Context,
|
||||
ownerUserID, friendUserID string,
|
||||
) (friend *relationTb.FriendModel, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
f.rcClient,
|
||||
f.getFriendKey(ownerUserID, friendUserID),
|
||||
f.expireTime,
|
||||
func(ctx context.Context) (*relationTb.FriendModel, error) {
|
||||
return f.friendDB.Take(ctx, ownerUserID, friendUserID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (f *FriendCacheRedis) DelFriend(ownerUserID, friendUserID string) FriendCache {
|
||||
|
||||
Vendored
+208
-66
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 cache
|
||||
|
||||
import (
|
||||
@@ -6,11 +20,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
unrelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -49,10 +64,22 @@ type GroupCache interface {
|
||||
GetJoinedGroupIDs(ctx context.Context, userID string) (joinedGroupIDs []string, err error)
|
||||
DelJoinedGroupID(userID ...string) GroupCache
|
||||
|
||||
GetGroupMemberInfo(ctx context.Context, groupID, userID string) (groupMember *relationTb.GroupMemberModel, err error)
|
||||
GetGroupMembersInfo(ctx context.Context, groupID string, userID []string) (groupMembers []*relationTb.GroupMemberModel, err error)
|
||||
GetGroupMemberInfo(
|
||||
ctx context.Context,
|
||||
groupID, userID string,
|
||||
) (groupMember *relationTb.GroupMemberModel, err error)
|
||||
GetGroupMembersInfo(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID []string,
|
||||
) (groupMembers []*relationTb.GroupMemberModel, err error)
|
||||
GetAllGroupMembersInfo(ctx context.Context, groupID string) (groupMembers []*relationTb.GroupMemberModel, err error)
|
||||
GetGroupMembersPage(ctx context.Context, groupID string, userID []string, showNumber, pageNumber int32) (total uint32, groupMembers []*relationTb.GroupMemberModel, err error)
|
||||
GetGroupMembersPage(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID []string,
|
||||
showNumber, pageNumber int32,
|
||||
) (total uint32, groupMembers []*relationTb.GroupMemberModel, err error)
|
||||
|
||||
DelGroupMembersInfo(groupID string, userID ...string) GroupCache
|
||||
|
||||
@@ -70,7 +97,14 @@ type GroupCacheRedis struct {
|
||||
rcClient *rockscache.Client
|
||||
}
|
||||
|
||||
func NewGroupCacheRedis(rdb redis.UniversalClient, groupDB relationTb.GroupModelInterface, groupMemberDB relationTb.GroupMemberModelInterface, groupRequestDB relationTb.GroupRequestModelInterface, mongoClient unrelationTb.SuperGroupModelInterface, opts rockscache.Options) GroupCache {
|
||||
func NewGroupCacheRedis(
|
||||
rdb redis.UniversalClient,
|
||||
groupDB relationTb.GroupModelInterface,
|
||||
groupMemberDB relationTb.GroupMemberModelInterface,
|
||||
groupRequestDB relationTb.GroupRequestModelInterface,
|
||||
mongoClient unrelationTb.SuperGroupModelInterface,
|
||||
opts rockscache.Options,
|
||||
) GroupCache {
|
||||
rcClient := rockscache.NewClient(rdb, opts)
|
||||
return &GroupCacheRedis{rcClient: rcClient, expireTime: groupExpireTime,
|
||||
groupDB: groupDB, groupMemberDB: groupMemberDB, groupRequestDB: groupRequestDB,
|
||||
@@ -79,7 +113,15 @@ func NewGroupCacheRedis(rdb redis.UniversalClient, groupDB relationTb.GroupModel
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) NewCache() GroupCache {
|
||||
return &GroupCacheRedis{rcClient: g.rcClient, expireTime: g.expireTime, groupDB: g.groupDB, groupMemberDB: g.groupMemberDB, groupRequestDB: g.groupRequestDB, mongoDB: g.mongoDB, metaCache: NewMetaCacheRedis(g.rcClient, g.metaCache.GetPreDelKeys()...)}
|
||||
return &GroupCacheRedis{
|
||||
rcClient: g.rcClient,
|
||||
expireTime: g.expireTime,
|
||||
groupDB: g.groupDB,
|
||||
groupMemberDB: g.groupMemberDB,
|
||||
groupRequestDB: g.groupRequestDB,
|
||||
mongoDB: g.mongoDB,
|
||||
metaCache: NewMetaCacheRedis(g.rcClient, g.metaCache.GetPreDelKeys()...),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) getGroupInfoKey(groupID string) string {
|
||||
@@ -135,20 +177,36 @@ func (g *GroupCacheRedis) GetGroupMemberIndex(groupMember *relationTb.GroupMembe
|
||||
}
|
||||
|
||||
// / groupInfo
|
||||
func (g *GroupCacheRedis) GetGroupsInfo(ctx context.Context, groupIDs []string) (groups []*relationTb.GroupModel, err error) {
|
||||
func (g *GroupCacheRedis) GetGroupsInfo(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
) (groups []*relationTb.GroupModel, err error) {
|
||||
var keys []string
|
||||
for _, group := range groupIDs {
|
||||
keys = append(keys, g.getGroupInfoKey(group))
|
||||
}
|
||||
return batchGetCache(ctx, g.rcClient, keys, g.expireTime, g.GetGroupIndex, func(ctx context.Context) ([]*relationTb.GroupModel, error) {
|
||||
return g.groupDB.Find(ctx, groupIDs)
|
||||
})
|
||||
return batchGetCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
keys,
|
||||
g.expireTime,
|
||||
g.GetGroupIndex,
|
||||
func(ctx context.Context) ([]*relationTb.GroupModel, error) {
|
||||
return g.groupDB.Find(ctx, groupIDs)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupInfo(ctx context.Context, groupID string) (group *relationTb.GroupModel, err error) {
|
||||
return getCache(ctx, g.rcClient, g.getGroupInfoKey(groupID), g.expireTime, func(ctx context.Context) (*relationTb.GroupModel, error) {
|
||||
return g.groupDB.Take(ctx, groupID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
g.getGroupInfoKey(groupID),
|
||||
g.expireTime,
|
||||
func(ctx context.Context) (*relationTb.GroupModel, error) {
|
||||
return g.groupDB.Take(ctx, groupID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) DelGroupsInfo(groupIDs ...string) GroupCache {
|
||||
@@ -161,31 +219,50 @@ func (g *GroupCacheRedis) DelGroupsInfo(groupIDs ...string) GroupCache {
|
||||
return new
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetJoinedSuperGroupIDs(ctx context.Context, userID string) (joinedSuperGroupIDs []string, err error) {
|
||||
return getCache(ctx, g.rcClient, g.getJoinedSuperGroupsIDKey(userID), g.expireTime, func(ctx context.Context) ([]string, error) {
|
||||
userGroup, err := g.mongoDB.GetSuperGroupByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return userGroup.GroupIDs, nil
|
||||
})
|
||||
func (g *GroupCacheRedis) GetJoinedSuperGroupIDs(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
) (joinedSuperGroupIDs []string, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
g.getJoinedSuperGroupsIDKey(userID),
|
||||
g.expireTime,
|
||||
func(ctx context.Context) ([]string, error) {
|
||||
userGroup, err := g.mongoDB.GetSuperGroupByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return userGroup.GroupIDs, nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetSuperGroupMemberIDs(ctx context.Context, groupIDs ...string) (models []*unrelationTb.SuperGroupModel, err error) {
|
||||
func (g *GroupCacheRedis) GetSuperGroupMemberIDs(
|
||||
ctx context.Context,
|
||||
groupIDs ...string,
|
||||
) (models []*unrelationTb.SuperGroupModel, err error) {
|
||||
var keys []string
|
||||
for _, group := range groupIDs {
|
||||
keys = append(keys, g.getSuperGroupMemberIDsKey(group))
|
||||
}
|
||||
return batchGetCache(ctx, g.rcClient, keys, g.expireTime, func(model *unrelationTb.SuperGroupModel, keys []string) (int, error) {
|
||||
for i, key := range keys {
|
||||
if g.getSuperGroupMemberIDsKey(model.GroupID) == key {
|
||||
return i, nil
|
||||
return batchGetCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
keys,
|
||||
g.expireTime,
|
||||
func(model *unrelationTb.SuperGroupModel, keys []string) (int, error) {
|
||||
for i, key := range keys {
|
||||
if g.getSuperGroupMemberIDsKey(model.GroupID) == key {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, errIndex
|
||||
}, func(ctx context.Context) ([]*unrelationTb.SuperGroupModel, error) {
|
||||
return g.mongoDB.FindSuperGroup(ctx, groupIDs)
|
||||
})
|
||||
return 0, errIndex
|
||||
},
|
||||
func(ctx context.Context) ([]*unrelationTb.SuperGroupModel, error) {
|
||||
return g.mongoDB.FindSuperGroup(ctx, groupIDs)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// userJoinSuperGroup
|
||||
@@ -211,19 +288,28 @@ func (g *GroupCacheRedis) DelSuperGroupMemberIDs(groupIDs ...string) GroupCache
|
||||
|
||||
// groupMembersHash
|
||||
func (g *GroupCacheRedis) GetGroupMembersHash(ctx context.Context, groupID string) (hashCode uint64, err error) {
|
||||
return getCache(ctx, g.rcClient, g.getGroupMembersHashKey(groupID), g.expireTime, func(ctx context.Context) (uint64, error) {
|
||||
userIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
utils.Sort(userIDs, true)
|
||||
bi := big.NewInt(0)
|
||||
bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16)
|
||||
return bi.Uint64(), nil
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
g.getGroupMembersHashKey(groupID),
|
||||
g.expireTime,
|
||||
func(ctx context.Context) (uint64, error) {
|
||||
userIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
utils.Sort(userIDs, true)
|
||||
bi := big.NewInt(0)
|
||||
bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16)
|
||||
return bi.Uint64(), nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupMemberHashMap(ctx context.Context, groupIDs []string) (map[string]*relationTb.GroupSimpleUserID, error) {
|
||||
func (g *GroupCacheRedis) GetGroupMemberHashMap(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
) (map[string]*relationTb.GroupSimpleUserID, error) {
|
||||
res := make(map[string]*relationTb.GroupSimpleUserID)
|
||||
for _, groupID := range groupIDs {
|
||||
hash, err := g.GetGroupMembersHash(ctx, groupID)
|
||||
@@ -247,9 +333,15 @@ func (g *GroupCacheRedis) DelGroupMembersHash(groupID string) GroupCache {
|
||||
|
||||
// groupMemberIDs
|
||||
func (g *GroupCacheRedis) GetGroupMemberIDs(ctx context.Context, groupID string) (groupMemberIDs []string, err error) {
|
||||
return getCache(ctx, g.rcClient, g.getGroupMemberIDsKey(groupID), g.expireTime, func(ctx context.Context) ([]string, error) {
|
||||
return g.groupMemberDB.FindMemberUserID(ctx, groupID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
g.getGroupMemberIDsKey(groupID),
|
||||
g.expireTime,
|
||||
func(ctx context.Context) ([]string, error) {
|
||||
return g.groupMemberDB.FindMemberUserID(ctx, groupID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupsMemberIDs(ctx context.Context, groupIDs []string) (map[string][]string, error) {
|
||||
@@ -271,9 +363,15 @@ func (g *GroupCacheRedis) DelGroupMemberIDs(groupID string) GroupCache {
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetJoinedGroupIDs(ctx context.Context, userID string) (joinedGroupIDs []string, err error) {
|
||||
return getCache(ctx, g.rcClient, g.getJoinedGroupsKey(userID), g.expireTime, func(ctx context.Context) ([]string, error) {
|
||||
return g.groupMemberDB.FindUserJoinedGroupID(ctx, userID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
g.getJoinedGroupsKey(userID),
|
||||
g.expireTime,
|
||||
func(ctx context.Context) ([]string, error) {
|
||||
return g.groupMemberDB.FindUserJoinedGroupID(ctx, userID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) DelJoinedGroupID(userIDs ...string) GroupCache {
|
||||
@@ -286,23 +384,48 @@ func (g *GroupCacheRedis) DelJoinedGroupID(userIDs ...string) GroupCache {
|
||||
return cache
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupMemberInfo(ctx context.Context, groupID, userID string) (groupMember *relationTb.GroupMemberModel, err error) {
|
||||
return getCache(ctx, g.rcClient, g.getGroupMemberInfoKey(groupID, userID), g.expireTime, func(ctx context.Context) (*relationTb.GroupMemberModel, error) {
|
||||
return g.groupMemberDB.Take(ctx, groupID, userID)
|
||||
})
|
||||
func (g *GroupCacheRedis) GetGroupMemberInfo(
|
||||
ctx context.Context,
|
||||
groupID, userID string,
|
||||
) (groupMember *relationTb.GroupMemberModel, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
g.getGroupMemberInfoKey(groupID, userID),
|
||||
g.expireTime,
|
||||
func(ctx context.Context) (*relationTb.GroupMemberModel, error) {
|
||||
return g.groupMemberDB.Take(ctx, groupID, userID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupMembersInfo(ctx context.Context, groupID string, userIDs []string) ([]*relationTb.GroupMemberModel, error) {
|
||||
func (g *GroupCacheRedis) GetGroupMembersInfo(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userIDs []string,
|
||||
) ([]*relationTb.GroupMemberModel, error) {
|
||||
var keys []string
|
||||
for _, userID := range userIDs {
|
||||
keys = append(keys, g.getGroupMemberInfoKey(groupID, userID))
|
||||
}
|
||||
return batchGetCache(ctx, g.rcClient, keys, g.expireTime, g.GetGroupMemberIndex, func(ctx context.Context) ([]*relationTb.GroupMemberModel, error) {
|
||||
return g.groupMemberDB.Find(ctx, []string{groupID}, userIDs, nil)
|
||||
})
|
||||
return batchGetCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
keys,
|
||||
g.expireTime,
|
||||
g.GetGroupMemberIndex,
|
||||
func(ctx context.Context) ([]*relationTb.GroupMemberModel, error) {
|
||||
return g.groupMemberDB.Find(ctx, []string{groupID}, userIDs, nil)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupMembersPage(ctx context.Context, groupID string, userIDs []string, showNumber, pageNumber int32) (total uint32, groupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
func (g *GroupCacheRedis) GetGroupMembersPage(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userIDs []string,
|
||||
showNumber, pageNumber int32,
|
||||
) (total uint32, groupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
groupMemberIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
@@ -316,7 +439,10 @@ func (g *GroupCacheRedis) GetGroupMembersPage(ctx context.Context, groupID strin
|
||||
return uint32(len(userIDs)), groupMembers, err
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetAllGroupMembersInfo(ctx context.Context, groupID string) (groupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
func (g *GroupCacheRedis) GetAllGroupMembersInfo(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
) (groupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
groupMemberIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -324,7 +450,10 @@ func (g *GroupCacheRedis) GetAllGroupMembersInfo(ctx context.Context, groupID st
|
||||
return g.GetGroupMembersInfo(ctx, groupID, groupMemberIDs)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetAllGroupMemberInfo(ctx context.Context, groupID string) ([]*relationTb.GroupMemberModel, error) {
|
||||
func (g *GroupCacheRedis) GetAllGroupMemberInfo(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
) ([]*relationTb.GroupMemberModel, error) {
|
||||
groupMemberIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -333,9 +462,16 @@ func (g *GroupCacheRedis) GetAllGroupMemberInfo(ctx context.Context, groupID str
|
||||
for _, groupMemberID := range groupMemberIDs {
|
||||
keys = append(keys, g.getGroupMemberInfoKey(groupID, groupMemberID))
|
||||
}
|
||||
return batchGetCache(ctx, g.rcClient, keys, g.expireTime, g.GetGroupMemberIndex, func(ctx context.Context) ([]*relationTb.GroupMemberModel, error) {
|
||||
return g.groupMemberDB.Find(ctx, []string{groupID}, groupMemberIDs, nil)
|
||||
})
|
||||
return batchGetCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
keys,
|
||||
g.expireTime,
|
||||
g.GetGroupMemberIndex,
|
||||
func(ctx context.Context) ([]*relationTb.GroupMemberModel, error) {
|
||||
return g.groupMemberDB.Find(ctx, []string{groupID}, groupMemberIDs, nil)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) DelGroupMembersInfo(groupID string, userIDs ...string) GroupCache {
|
||||
@@ -349,9 +485,15 @@ func (g *GroupCacheRedis) DelGroupMembersInfo(groupID string, userIDs ...string)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupMemberNum(ctx context.Context, groupID string) (memberNum int64, err error) {
|
||||
return getCache(ctx, g.rcClient, g.getGroupMemberNumKey(groupID), g.expireTime, func(ctx context.Context) (int64, error) {
|
||||
return g.groupMemberDB.TakeGroupMemberNum(ctx, groupID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
g.getGroupMemberNumKey(groupID),
|
||||
g.expireTime,
|
||||
func(ctx context.Context) (int64, error) {
|
||||
return g.groupMemberDB.TakeGroupMemberNum(ctx, groupID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) DelGroupsMemberNum(groupID ...string) GroupCache {
|
||||
|
||||
Vendored
+16
-1
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 cache
|
||||
|
||||
import (
|
||||
@@ -6,10 +20,11 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func NewRedis() (redis.UniversalClient, error) {
|
||||
|
||||
Vendored
+47
-5
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 cache
|
||||
|
||||
import (
|
||||
@@ -7,10 +21,11 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -48,7 +63,14 @@ func (m *metaCacheRedis) ExecDel(ctx context.Context) error {
|
||||
for {
|
||||
if err := m.rcClient.TagAsDeletedBatch2(ctx, m.keys); err != nil {
|
||||
if retryTimes >= m.maxRetryTimes {
|
||||
err = errs.ErrInternalServer.Wrap(fmt.Sprintf("delete cache error: %v, keys: %v, retry times %d, please check redis server", err, m.keys, retryTimes))
|
||||
err = errs.ErrInternalServer.Wrap(
|
||||
fmt.Sprintf(
|
||||
"delete cache error: %v, keys: %v, retry times %d, please check redis server",
|
||||
err,
|
||||
m.keys,
|
||||
retryTimes,
|
||||
),
|
||||
)
|
||||
log.ZWarn(ctx, "delete cache failed, please handle keys", err, "keys", m.keys)
|
||||
return err
|
||||
}
|
||||
@@ -84,7 +106,13 @@ func GetDefaultOpt() rockscache.Options {
|
||||
return opts
|
||||
}
|
||||
|
||||
func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key string, expire time.Duration, fn func(ctx context.Context) (T, error)) (T, error) {
|
||||
func getCache[T any](
|
||||
ctx context.Context,
|
||||
rcClient *rockscache.Client,
|
||||
key string,
|
||||
expire time.Duration,
|
||||
fn func(ctx context.Context) (T, error),
|
||||
) (T, error) {
|
||||
var t T
|
||||
var write bool
|
||||
v, err := rcClient.Fetch2(ctx, key, expire, func() (s string, err error) {
|
||||
@@ -116,7 +144,14 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func batchGetCache[T any](ctx context.Context, rcClient *rockscache.Client, keys []string, expire time.Duration, keyIndexFn func(t T, keys []string) (int, error), fn func(ctx context.Context) ([]T, error)) ([]T, error) {
|
||||
func batchGetCache[T any](
|
||||
ctx context.Context,
|
||||
rcClient *rockscache.Client,
|
||||
keys []string,
|
||||
expire time.Duration,
|
||||
keyIndexFn func(t T, keys []string) (int, error),
|
||||
fn func(ctx context.Context) ([]T, error),
|
||||
) ([]T, error) {
|
||||
batchMap, err := rcClient.FetchBatch2(ctx, keys, expire, func(idxs []int) (m map[int]string, err error) {
|
||||
values := make(map[int]string)
|
||||
tArrays, err := fn(ctx)
|
||||
@@ -153,7 +188,14 @@ func batchGetCache[T any](ctx context.Context, rcClient *rockscache.Client, keys
|
||||
return tArrays, nil
|
||||
}
|
||||
|
||||
func batchGetCacheMap[T any](ctx context.Context, rcClient *rockscache.Client, keys, originKeys []string, expire time.Duration, keyIndexFn func(s string, keys []string) (int, error), fn func(ctx context.Context) (map[string]T, error)) (map[string]T, error) {
|
||||
func batchGetCacheMap[T any](
|
||||
ctx context.Context,
|
||||
rcClient *rockscache.Client,
|
||||
keys, originKeys []string,
|
||||
expire time.Duration,
|
||||
keyIndexFn func(s string, keys []string) (int, error),
|
||||
fn func(ctx context.Context) (map[string]T, error),
|
||||
) (map[string]T, error) {
|
||||
batchMap, err := rcClient.FetchBatch2(ctx, keys, expire, func(idxs []int) (m map[int]string, err error) {
|
||||
tArrays, err := fn(ctx)
|
||||
if err != nil {
|
||||
|
||||
Vendored
+123
-20
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 cache
|
||||
|
||||
import (
|
||||
@@ -5,16 +19,18 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
@@ -86,7 +102,11 @@ type MsgModel interface {
|
||||
GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error)
|
||||
SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error
|
||||
DeleteTokenByUidPid(ctx context.Context, userID string, platformID int, fields []string) error
|
||||
GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsg []*sdkws.MsgData, failedSeqList []int64, err error)
|
||||
GetMessagesBySeq(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
seqs []int64,
|
||||
) (seqMsg []*sdkws.MsgData, failedSeqList []int64, err error)
|
||||
SetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error)
|
||||
UserDeleteMsgs(ctx context.Context, conversationID string, seqs []int64, userID string) error
|
||||
DelUserDeleteMsgsList(ctx context.Context, conversationID string, seqs []int64)
|
||||
@@ -99,7 +119,12 @@ type MsgModel interface {
|
||||
JudgeMessageReactionExist(ctx context.Context, clientMsgID string, sessionType int32) (bool, error)
|
||||
GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error)
|
||||
DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error
|
||||
SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error)
|
||||
SetMessageReactionExpire(
|
||||
ctx context.Context,
|
||||
clientMsgID string,
|
||||
sessionType int32,
|
||||
expiration time.Duration,
|
||||
) (bool, error)
|
||||
GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error)
|
||||
SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error
|
||||
LockMessageTypeKey(ctx context.Context, clientMsgID string, TypeKey string) error
|
||||
@@ -130,15 +155,28 @@ func (c *msgCache) getHasReadSeqKey(conversationID string, userID string) string
|
||||
return hasReadSeq + userID + ":" + conversationID
|
||||
}
|
||||
|
||||
func (c *msgCache) setSeq(ctx context.Context, conversationID string, seq int64, getkey func(conversationID string) string) error {
|
||||
func (c *msgCache) setSeq(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
seq int64,
|
||||
getkey func(conversationID string) string,
|
||||
) error {
|
||||
return utils.Wrap1(c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err())
|
||||
}
|
||||
|
||||
func (c *msgCache) getSeq(ctx context.Context, conversationID string, getkey func(conversationID string) string) (int64, error) {
|
||||
func (c *msgCache) getSeq(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
getkey func(conversationID string) string,
|
||||
) (int64, error) {
|
||||
return utils.Wrap2(c.rdb.Get(ctx, getkey(conversationID)).Int64())
|
||||
}
|
||||
|
||||
func (c *msgCache) getSeqs(ctx context.Context, items []string, getkey func(s string) string) (m map[string]int64, err error) {
|
||||
func (c *msgCache) getSeqs(
|
||||
ctx context.Context,
|
||||
items []string,
|
||||
getkey func(s string) string,
|
||||
) (m map[string]int64, err error) {
|
||||
pipe := c.rdb.Pipeline()
|
||||
for _, v := range items {
|
||||
if err := pipe.Get(ctx, getkey(v)).Err(); err != nil && err != redis.Nil {
|
||||
@@ -209,16 +247,30 @@ func (c *msgCache) GetConversationUserMinSeq(ctx context.Context, conversationID
|
||||
return utils.Wrap2(c.rdb.Get(ctx, c.getConversationUserMinSeqKey(conversationID, userID)).Int64())
|
||||
}
|
||||
|
||||
func (c *msgCache) GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (m map[string]int64, err error) {
|
||||
func (c *msgCache) GetConversationUserMinSeqs(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
userIDs []string,
|
||||
) (m map[string]int64, err error) {
|
||||
return c.getSeqs(ctx, userIDs, func(userID string) string {
|
||||
return c.getConversationUserMinSeqKey(conversationID, userID)
|
||||
})
|
||||
}
|
||||
func (c *msgCache) SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error {
|
||||
|
||||
func (c *msgCache) SetConversationUserMinSeq(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
userID string,
|
||||
minSeq int64,
|
||||
) error {
|
||||
return utils.Wrap1(c.rdb.Set(ctx, c.getConversationUserMinSeqKey(conversationID, userID), minSeq, 0).Err())
|
||||
}
|
||||
|
||||
func (c *msgCache) SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error) {
|
||||
func (c *msgCache) SetConversationUserMinSeqs(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
seqs map[string]int64,
|
||||
) (err error) {
|
||||
return c.setSeqs(ctx, seqs, func(userID string) string {
|
||||
return c.getConversationUserMinSeqKey(conversationID, userID)
|
||||
})
|
||||
@@ -246,7 +298,11 @@ func (c *msgCache) UserSetHasReadSeqs(ctx context.Context, userID string, hasRea
|
||||
})
|
||||
}
|
||||
|
||||
func (c *msgCache) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) {
|
||||
func (c *msgCache) GetHasReadSeqs(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
conversationIDs []string,
|
||||
) (map[string]int64, error) {
|
||||
return c.getSeqs(ctx, conversationIDs, func(conversationID string) string {
|
||||
return c.getHasReadSeqKey(conversationID, userID)
|
||||
})
|
||||
@@ -296,7 +352,11 @@ func (c *msgCache) allMessageCacheKey(conversationID string) string {
|
||||
return messageCache + conversationID + "_*"
|
||||
}
|
||||
|
||||
func (c *msgCache) GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
|
||||
func (c *msgCache) GetMessagesBySeq(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
seqs []int64,
|
||||
) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
|
||||
pipe := c.rdb.Pipeline()
|
||||
for _, v := range seqs {
|
||||
//MESSAGE_CACHE:169.254.225.224_reliability1653387820_0_1
|
||||
@@ -404,7 +464,17 @@ func (c *msgCache) DelUserDeleteMsgsList(ctx context.Context, conversationID str
|
||||
err = pipe.SRem(ctx, c.getUserDelList(conversationID, userID), seq).Err()
|
||||
if err != nil {
|
||||
failedFlag = true
|
||||
log.ZWarn(ctx, "DelUserDeleteMsgsList failed", err, "conversationID", conversationID, "seq", seq, "userID", userID)
|
||||
log.ZWarn(
|
||||
ctx,
|
||||
"DelUserDeleteMsgsList failed",
|
||||
err,
|
||||
"conversationID",
|
||||
conversationID,
|
||||
"seq",
|
||||
seq,
|
||||
"userID",
|
||||
userID,
|
||||
)
|
||||
}
|
||||
}
|
||||
if !failedFlag {
|
||||
@@ -499,8 +569,17 @@ func (c *msgCache) GetSendMsgStatus(ctx context.Context, id string) (int32, erro
|
||||
return int32(result), errs.Wrap(err)
|
||||
}
|
||||
|
||||
func (c *msgCache) SetFcmToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) (err error) {
|
||||
return errs.Wrap(c.rdb.Set(ctx, fcmToken+account+":"+strconv.Itoa(platformID), fcmToken, time.Duration(expireTime)*time.Second).Err())
|
||||
func (c *msgCache) SetFcmToken(
|
||||
ctx context.Context,
|
||||
account string,
|
||||
platformID int,
|
||||
fcmToken string,
|
||||
expireTime int64,
|
||||
) (err error) {
|
||||
return errs.Wrap(
|
||||
c.rdb.Set(ctx, fcmToken+account+":"+strconv.Itoa(platformID), fcmToken, time.Duration(expireTime)*time.Second).
|
||||
Err(),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *msgCache) GetFcmToken(ctx context.Context, account string, platformID int) (string, error) {
|
||||
@@ -556,22 +635,46 @@ func (c *msgCache) JudgeMessageReactionExist(ctx context.Context, clientMsgID st
|
||||
return n > 0, nil
|
||||
}
|
||||
|
||||
func (c *msgCache) SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error {
|
||||
func (c *msgCache) SetMessageTypeKeyValue(
|
||||
ctx context.Context,
|
||||
clientMsgID string,
|
||||
sessionType int32,
|
||||
typeKey, value string,
|
||||
) error {
|
||||
return errs.Wrap(c.rdb.HSet(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), typeKey, value).Err())
|
||||
}
|
||||
|
||||
func (c *msgCache) SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error) {
|
||||
func (c *msgCache) SetMessageReactionExpire(
|
||||
ctx context.Context,
|
||||
clientMsgID string,
|
||||
sessionType int32,
|
||||
expiration time.Duration,
|
||||
) (bool, error) {
|
||||
return utils.Wrap2(c.rdb.Expire(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), expiration).Result())
|
||||
}
|
||||
|
||||
func (c *msgCache) GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error) {
|
||||
func (c *msgCache) GetMessageTypeKeyValue(
|
||||
ctx context.Context,
|
||||
clientMsgID string,
|
||||
sessionType int32,
|
||||
typeKey string,
|
||||
) (string, error) {
|
||||
return utils.Wrap2(c.rdb.HGet(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), typeKey).Result())
|
||||
}
|
||||
|
||||
func (c *msgCache) GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error) {
|
||||
func (c *msgCache) GetOneMessageAllReactionList(
|
||||
ctx context.Context,
|
||||
clientMsgID string,
|
||||
sessionType int32,
|
||||
) (map[string]string, error) {
|
||||
return utils.Wrap2(c.rdb.HGetAll(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType)).Result())
|
||||
}
|
||||
|
||||
func (c *msgCache) DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error {
|
||||
func (c *msgCache) DeleteOneMessageKey(
|
||||
ctx context.Context,
|
||||
clientMsgID string,
|
||||
sessionType int32,
|
||||
subKey string,
|
||||
) error {
|
||||
return errs.Wrap(c.rdb.HDel(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), subKey).Err())
|
||||
}
|
||||
|
||||
Vendored
+55
-17
@@ -1,12 +1,27 @@
|
||||
// Copyright © 2023 OpenIM. 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 cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,7 +47,11 @@ type UserCacheRedis struct {
|
||||
rcClient *rockscache.Client
|
||||
}
|
||||
|
||||
func NewUserCacheRedis(rdb redis.UniversalClient, userDB relationTb.UserModelInterface, options rockscache.Options) UserCache {
|
||||
func NewUserCacheRedis(
|
||||
rdb redis.UniversalClient,
|
||||
userDB relationTb.UserModelInterface,
|
||||
options rockscache.Options,
|
||||
) UserCache {
|
||||
rcClient := rockscache.NewClient(rdb, options)
|
||||
return &UserCacheRedis{
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
@@ -60,9 +79,15 @@ func (u *UserCacheRedis) getUserGlobalRecvMsgOptKey(userID string) string {
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) GetUserInfo(ctx context.Context, userID string) (userInfo *relationTb.UserModel, err error) {
|
||||
return getCache(ctx, u.rcClient, u.getUserInfoKey(userID), u.expireTime, func(ctx context.Context) (*relationTb.UserModel, error) {
|
||||
return u.userDB.Take(ctx, userID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
u.rcClient,
|
||||
u.getUserInfoKey(userID),
|
||||
u.expireTime,
|
||||
func(ctx context.Context) (*relationTb.UserModel, error) {
|
||||
return u.userDB.Take(ctx, userID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) GetUsersInfo(ctx context.Context, userIDs []string) ([]*relationTb.UserModel, error) {
|
||||
@@ -70,16 +95,23 @@ func (u *UserCacheRedis) GetUsersInfo(ctx context.Context, userIDs []string) ([]
|
||||
for _, userID := range userIDs {
|
||||
keys = append(keys, u.getUserInfoKey(userID))
|
||||
}
|
||||
return batchGetCache(ctx, u.rcClient, keys, u.expireTime, func(user *relationTb.UserModel, keys []string) (int, error) {
|
||||
for i, key := range keys {
|
||||
if key == u.getUserInfoKey(user.UserID) {
|
||||
return i, nil
|
||||
return batchGetCache(
|
||||
ctx,
|
||||
u.rcClient,
|
||||
keys,
|
||||
u.expireTime,
|
||||
func(user *relationTb.UserModel, keys []string) (int, error) {
|
||||
for i, key := range keys {
|
||||
if key == u.getUserInfoKey(user.UserID) {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, errIndex
|
||||
}, func(ctx context.Context) ([]*relationTb.UserModel, error) {
|
||||
return u.userDB.Find(ctx, userIDs)
|
||||
})
|
||||
return 0, errIndex
|
||||
},
|
||||
func(ctx context.Context) ([]*relationTb.UserModel, error) {
|
||||
return u.userDB.Find(ctx, userIDs)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) DelUsersInfo(userIDs ...string) UserCache {
|
||||
@@ -93,9 +125,15 @@ func (u *UserCacheRedis) DelUsersInfo(userIDs ...string) UserCache {
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) {
|
||||
return getCache(ctx, u.rcClient, u.getUserGlobalRecvMsgOptKey(userID), u.expireTime, func(ctx context.Context) (int, error) {
|
||||
return u.userDB.GetUserGlobalRecvMsgOpt(ctx, userID)
|
||||
})
|
||||
return getCache(
|
||||
ctx,
|
||||
u.rcClient,
|
||||
u.getUserGlobalRecvMsgOptKey(userID),
|
||||
u.expireTime,
|
||||
func(ctx context.Context) (int, error) {
|
||||
return u.userDB.GetUserGlobalRecvMsgOpt(ctx, userID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) DelUsersGlobalRecvMsgOpt(userIDs ...string) UserCache {
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/tokenverify"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
type AuthDatabase interface {
|
||||
@@ -29,7 +44,11 @@ func NewAuthDatabase(cache cache.MsgModel, accessSecret string, accessExpire int
|
||||
}
|
||||
|
||||
// 结果为空 不返回错误
|
||||
func (a *authDatabase) GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) {
|
||||
func (a *authDatabase) GetTokensWithoutError(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
platformID int,
|
||||
) (map[string]int, error) {
|
||||
return a.cache.GetTokensWithoutError(ctx, userID, platformID)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
@@ -15,7 +29,11 @@ type BlackDatabase interface {
|
||||
// Delete 删除黑名单
|
||||
Delete(ctx context.Context, blacks []*relation.BlackModel) (err error)
|
||||
// FindOwnerBlacks 获取黑名单列表
|
||||
FindOwnerBlacks(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (blacks []*relation.BlackModel, total int64, err error)
|
||||
FindOwnerBlacks(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (blacks []*relation.BlackModel, total int64, err error)
|
||||
FindBlackIDs(ctx context.Context, ownerUserID string) (blackIDs []string, err error)
|
||||
// CheckIn 检查user2是否在user1的黑名单列表中(inUser1Blacks==true) 检查user1是否在user2的黑名单列表中(inUser2Blacks==true)
|
||||
CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Blacks bool, inUser2Blacks bool, err error)
|
||||
@@ -55,12 +73,19 @@ func (b *blackDatabase) deleteBlackIDsCache(ctx context.Context, blacks []*relat
|
||||
}
|
||||
|
||||
// FindOwnerBlacks 获取黑名单列表
|
||||
func (b *blackDatabase) FindOwnerBlacks(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (blacks []*relation.BlackModel, total int64, err error) {
|
||||
func (b *blackDatabase) FindOwnerBlacks(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (blacks []*relation.BlackModel, total int64, err error) {
|
||||
return b.black.FindOwnerBlacks(ctx, ownerUserID, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
// CheckIn 检查user2是否在user1的黑名单列表中(inUser1Blacks==true) 检查user1是否在user2的黑名单列表中(inUser2Blacks==true)
|
||||
func (b *blackDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Blacks bool, inUser2Blacks bool, err error) {
|
||||
func (b *blackDatabase) CheckIn(
|
||||
ctx context.Context,
|
||||
userID1, userID2 string,
|
||||
) (inUser1Blacks bool, inUser2Blacks bool, err error) {
|
||||
userID1BlackIDs, err := b.cache.GetBlackIDs(ctx, userID1)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
@@ -7,7 +21,11 @@ import (
|
||||
|
||||
type ChatLogDatabase interface {
|
||||
CreateChatLog(msg *pbMsg.MsgDataToMQ) error
|
||||
GetChatLog(chatLog *relationTb.ChatLogModel, pageNumber, showNumber int32, contentTypes []int32) (int64, []relationTb.ChatLogModel, error)
|
||||
GetChatLog(
|
||||
chatLog *relationTb.ChatLogModel,
|
||||
pageNumber, showNumber int32,
|
||||
contentTypes []int32,
|
||||
) (int64, []relationTb.ChatLogModel, error)
|
||||
}
|
||||
|
||||
func NewChatLogDatabase(chatLogModelInterface relationTb.ChatLogModelInterface) ChatLogDatabase {
|
||||
@@ -22,6 +40,10 @@ func (c *chatLogDatabase) CreateChatLog(msg *pbMsg.MsgDataToMQ) error {
|
||||
return c.chatLogModel.Create(msg)
|
||||
}
|
||||
|
||||
func (c *chatLogDatabase) GetChatLog(chatLog *relationTb.ChatLogModel, pageNumber, showNumber int32, contentTypes []int32) (int64, []relationTb.ChatLogModel, error) {
|
||||
func (c *chatLogDatabase) GetChatLog(
|
||||
chatLog *relationTb.ChatLogModel,
|
||||
pageNumber, showNumber int32,
|
||||
contentTypes []int32,
|
||||
) (int64, []relationTb.ChatLogModel, error) {
|
||||
return c.chatLogModel.GetChatLog(chatLog, pageNumber, showNumber, contentTypes)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
@@ -34,6 +35,7 @@ type ConversationDatabase interface {
|
||||
GetAllConversationIDs(ctx context.Context) ([]string, error)
|
||||
GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error)
|
||||
GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error)
|
||||
GetConversationIDsNeedDestruct(ctx context.Context) ([]*relationTb.ConversationModel, error)
|
||||
}
|
||||
|
||||
func NewConversationDatabase(conversation relationTb.ConversationModelInterface, cache cache.ConversationCache, tx tx.Tx) ConversationDatabase {
|
||||
@@ -73,12 +75,14 @@ func (c *conversationDatabase) SetUsersConversationFiledTx(ctx context.Context,
|
||||
NotUserIDs := utils.DifferenceString(haveUserIDs, userIDs)
|
||||
log.ZDebug(ctx, "SetUsersConversationFiledTx", "NotUserIDs", NotUserIDs, "haveUserIDs", haveUserIDs, "userIDs", userIDs)
|
||||
var conversations []*relationTb.ConversationModel
|
||||
now := time.Now()
|
||||
for _, v := range NotUserIDs {
|
||||
temp := new(relationTb.ConversationModel)
|
||||
if err := utils.CopyStructFields(temp, conversation); err != nil {
|
||||
return err
|
||||
}
|
||||
temp.OwnerUserID = v
|
||||
temp.CreateTime = now
|
||||
conversations = append(conversations, temp)
|
||||
|
||||
}
|
||||
@@ -123,26 +127,28 @@ func (c *conversationDatabase) SyncPeerUserPrivateConversationTx(ctx context.Con
|
||||
conversationTx := c.conversationDB.NewTx(tx)
|
||||
for _, conversation := range conversations {
|
||||
for _, v := range [][2]string{{conversation.OwnerUserID, conversation.UserID}, {conversation.UserID, conversation.OwnerUserID}} {
|
||||
haveUserIDs, err := conversationTx.FindUserID(ctx, []string{v[0]}, []string{conversation.ConversationID})
|
||||
ownerUserID := v[0]
|
||||
userID := v[1]
|
||||
haveUserIDs, err := conversationTx.FindUserID(ctx, []string{ownerUserID}, []string{conversation.ConversationID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(haveUserIDs) > 0 {
|
||||
_, err := conversationTx.UpdateByMap(ctx, []string{v[0]}, conversation.ConversationID, map[string]interface{}{"is_private_chat": conversation.IsPrivateChat})
|
||||
_, err := conversationTx.UpdateByMap(ctx, []string{ownerUserID}, conversation.ConversationID, map[string]interface{}{"is_private_chat": conversation.IsPrivateChat})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache = cache.DelUsersConversation(conversation.ConversationID, v[0])
|
||||
cache = cache.DelUsersConversation(conversation.ConversationID, ownerUserID)
|
||||
} else {
|
||||
newConversation := *conversation
|
||||
newConversation.OwnerUserID = v[0]
|
||||
newConversation.UserID = v[1]
|
||||
newConversation.OwnerUserID = ownerUserID
|
||||
newConversation.UserID = userID
|
||||
newConversation.ConversationID = conversation.ConversationID
|
||||
newConversation.IsPrivateChat = conversation.IsPrivateChat
|
||||
if err := conversationTx.Create(ctx, []*relationTb.ConversationModel{&newConversation}); err != nil {
|
||||
return err
|
||||
}
|
||||
cache = cache.DelConversationIDs(v[0]).DelUserConversationIDsHash(v[0])
|
||||
cache = cache.DelConversationIDs(ownerUserID).DelUserConversationIDsHash(ownerUserID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +156,7 @@ func (c *conversationDatabase) SyncPeerUserPrivateConversationTx(ctx context.Con
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.cache.ExecDel(ctx)
|
||||
return cache.ExecDel(ctx)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) FindConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationTb.ConversationModel, error) {
|
||||
@@ -270,3 +276,7 @@ func (c *conversationDatabase) GetUserAllHasReadSeqs(ctx context.Context, ownerU
|
||||
func (c *conversationDatabase) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationTb.ConversationModel, error) {
|
||||
return c.conversationDB.GetConversationsByConversationID(ctx, conversationIDs)
|
||||
}
|
||||
|
||||
func (c *conversationDatabase) GetConversationIDsNeedDestruct(ctx context.Context) ([]*relationTb.ConversationModel, error) {
|
||||
return c.conversationDB.GetConversationIDsNeedDestruct(ctx)
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/tx"
|
||||
)
|
||||
|
||||
// for mongoDB
|
||||
type ExtendMsgDatabase interface {
|
||||
CreateExtendMsgSet(ctx context.Context, set *unRelationTb.ExtendMsgSetModel) error
|
||||
GetAllExtendMsgSet(ctx context.Context, ID string, opts *unRelationTb.GetAllExtendMsgSetOpts) (sets []*unRelationTb.ExtendMsgSetModel, err error)
|
||||
GetExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, maxMsgUpdateTime int64) (*unRelationTb.ExtendMsgSetModel, error)
|
||||
InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *unRelationTb.ExtendMsgModel) error
|
||||
InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error
|
||||
DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error
|
||||
GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (extendMsg *unRelationTb.ExtendMsgModel, err error)
|
||||
}
|
||||
|
||||
type extendMsgDatabase struct {
|
||||
database unRelationTb.ExtendMsgSetModelInterface
|
||||
cache cache.ExtendMsgSetCache
|
||||
ctxTx tx.CtxTx
|
||||
}
|
||||
|
||||
func NewExtendMsgDatabase(extendMsgModel unRelationTb.ExtendMsgSetModelInterface, cache cache.ExtendMsgSetCache, ctxTx tx.CtxTx) ExtendMsgDatabase {
|
||||
return &extendMsgDatabase{database: extendMsgModel, cache: cache, ctxTx: ctxTx}
|
||||
}
|
||||
|
||||
func (e *extendMsgDatabase) CreateExtendMsgSet(ctx context.Context, set *unRelationTb.ExtendMsgSetModel) error {
|
||||
return e.database.CreateExtendMsgSet(ctx, set)
|
||||
}
|
||||
|
||||
func (e *extendMsgDatabase) GetAllExtendMsgSet(ctx context.Context, conversationID string, opts *unRelationTb.GetAllExtendMsgSetOpts) (sets []*unRelationTb.ExtendMsgSetModel, err error) {
|
||||
return e.database.GetAllExtendMsgSet(ctx, conversationID, opts)
|
||||
}
|
||||
|
||||
func (e *extendMsgDatabase) GetExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, maxMsgUpdateTime int64) (*unRelationTb.ExtendMsgSetModel, error) {
|
||||
return e.database.GetExtendMsgSet(ctx, conversationID, sessionType, maxMsgUpdateTime)
|
||||
}
|
||||
|
||||
func (e *extendMsgDatabase) InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *unRelationTb.ExtendMsgModel) error {
|
||||
return e.database.InsertExtendMsg(ctx, conversationID, sessionType, msg)
|
||||
}
|
||||
|
||||
func (e *extendMsgDatabase) InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error {
|
||||
return e.database.InsertOrUpdateReactionExtendMsgSet(ctx, conversationID, sessionType, clientMsgID, msgFirstModifyTime, reactionExtensionList)
|
||||
}
|
||||
|
||||
func (e *extendMsgDatabase) DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error {
|
||||
return e.database.DeleteReactionExtendMsgSet(ctx, conversationID, sessionType, clientMsgID, msgFirstModifyTime, reactionExtensionList)
|
||||
}
|
||||
|
||||
func (e *extendMsgDatabase) GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (extendMsg *unRelationTb.ExtendMsgModel, err error) {
|
||||
return e.cache.GetExtendMsg(ctx, conversationID, sessionType, clientMsgID, maxMsgUpdateTime)
|
||||
}
|
||||
@@ -1,9 +1,25 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
@@ -11,7 +27,6 @@ import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type FriendDatabase interface {
|
||||
@@ -30,15 +45,35 @@ type FriendDatabase interface {
|
||||
// 更新好友备注
|
||||
UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error)
|
||||
// 获取ownerUserID的好友列表
|
||||
PageOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error)
|
||||
PageOwnerFriends(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendModel, total int64, err error)
|
||||
// friendUserID在哪些人的好友列表中
|
||||
PageInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error)
|
||||
PageInWhoseFriends(
|
||||
ctx context.Context,
|
||||
friendUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendModel, total int64, err error)
|
||||
// 获取我发出去的好友申请
|
||||
PageFriendRequestFromMe(ctx context.Context, userID string, pageNumber, showNumber int32) (friends []*relation.FriendRequestModel, total int64, err error)
|
||||
PageFriendRequestFromMe(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendRequestModel, total int64, err error)
|
||||
// 获取我收到的的好友申请
|
||||
PageFriendRequestToMe(ctx context.Context, userID string, pageNumber, showNumber int32) (friends []*relation.FriendRequestModel, total int64, err error)
|
||||
PageFriendRequestToMe(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendRequestModel, total int64, err error)
|
||||
// 获取某人指定好友的信息
|
||||
FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*relation.FriendModel, err error)
|
||||
FindFriendsWithError(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
friendUserIDs []string,
|
||||
) (friends []*relation.FriendModel, err error)
|
||||
FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error)
|
||||
}
|
||||
|
||||
@@ -49,12 +84,20 @@ type friendDatabase struct {
|
||||
cache cache.FriendCache
|
||||
}
|
||||
|
||||
func NewFriendDatabase(friend relation.FriendModelInterface, friendRequest relation.FriendRequestModelInterface, cache cache.FriendCache, tx tx.Tx) FriendDatabase {
|
||||
func NewFriendDatabase(
|
||||
friend relation.FriendModelInterface,
|
||||
friendRequest relation.FriendRequestModelInterface,
|
||||
cache cache.FriendCache,
|
||||
tx tx.Tx,
|
||||
) FriendDatabase {
|
||||
return &friendDatabase{friend: friend, friendRequest: friendRequest, cache: cache, tx: tx}
|
||||
}
|
||||
|
||||
// ok 检查user2是否在user1的好友列表中(inUser1Friends==true) 检查user1是否在user2的好友列表中(inUser2Friends==true)
|
||||
func (f *friendDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Friends bool, inUser2Friends bool, err error) {
|
||||
func (f *friendDatabase) CheckIn(
|
||||
ctx context.Context,
|
||||
userID1, userID2 string,
|
||||
) (inUser1Friends bool, inUser2Friends bool, err error) {
|
||||
userID1FriendIDs, err := f.cache.GetFriendIDs(ctx, userID1)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -67,7 +110,12 @@ func (f *friendDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (
|
||||
}
|
||||
|
||||
// 增加或者更新好友申请 如果之前有记录则更新,没有记录则新增
|
||||
func (f *friendDatabase) AddFriendRequest(ctx context.Context, fromUserID, toUserID string, reqMsg string, ex string) (err error) {
|
||||
func (f *friendDatabase) AddFriendRequest(
|
||||
ctx context.Context,
|
||||
fromUserID, toUserID string,
|
||||
reqMsg string,
|
||||
ex string,
|
||||
) (err error) {
|
||||
return f.tx.Transaction(func(tx any) error {
|
||||
_, err := f.friendRequest.NewTx(tx).Take(ctx, fromUserID, toUserID)
|
||||
//有db错误
|
||||
@@ -96,7 +144,12 @@ func (f *friendDatabase) AddFriendRequest(ctx context.Context, fromUserID, toUse
|
||||
}
|
||||
|
||||
// (1)先判断是否在好友表 (在不在都不返回错误) (2)对于不在好友列表的 插入即可
|
||||
func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, addSource int32) (err error) {
|
||||
func (f *friendDatabase) BecomeFriends(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
friendUserIDs []string,
|
||||
addSource int32,
|
||||
) (err error) {
|
||||
cache := f.cache.NewCache()
|
||||
if err := f.tx.Transaction(func(tx any) error {
|
||||
//先find 找出重复的 去掉重复的
|
||||
@@ -142,7 +195,10 @@ func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string,
|
||||
}
|
||||
|
||||
// 拒绝好友申请 (1)检查是否有申请记录且为未处理状态 (没有记录返回错误) (2)修改申请记录 已拒绝
|
||||
func (f *friendDatabase) RefuseFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) {
|
||||
func (f *friendDatabase) RefuseFriendRequest(
|
||||
ctx context.Context,
|
||||
friendRequest *relation.FriendRequestModel,
|
||||
) (err error) {
|
||||
fr, err := f.friendRequest.Take(ctx, friendRequest.FromUserID, friendRequest.ToUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -160,7 +216,10 @@ func (f *friendDatabase) RefuseFriendRequest(ctx context.Context, friendRequest
|
||||
}
|
||||
|
||||
// AgreeFriendRequest 同意好友申请 (1)检查是否有申请记录且为未处理状态 (没有记录返回错误) (2)检查是否好友(不返回错误) (3) 建立双向好友关系(存在的忽略)
|
||||
func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) {
|
||||
func (f *friendDatabase) AgreeFriendRequest(
|
||||
ctx context.Context,
|
||||
friendRequest *relation.FriendRequestModel,
|
||||
) (err error) {
|
||||
return f.tx.Transaction(func(tx any) error {
|
||||
fr, err := f.friendRequest.NewTx(tx).Take(ctx, friendRequest.FromUserID, friendRequest.ToUserID)
|
||||
if err != nil {
|
||||
@@ -185,10 +244,26 @@ func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest *
|
||||
}))
|
||||
var adds []*relation.FriendModel
|
||||
if _, ok := existsMap[[...]string{friendRequest.ToUserID, friendRequest.FromUserID}]; !ok { // 自己 - 好友
|
||||
adds = append(adds, &relation.FriendModel{OwnerUserID: friendRequest.ToUserID, FriendUserID: friendRequest.FromUserID, AddSource: int32(constant.BecomeFriendByApply), OperatorUserID: friendRequest.FromUserID})
|
||||
adds = append(
|
||||
adds,
|
||||
&relation.FriendModel{
|
||||
OwnerUserID: friendRequest.ToUserID,
|
||||
FriendUserID: friendRequest.FromUserID,
|
||||
AddSource: int32(constant.BecomeFriendByApply),
|
||||
OperatorUserID: friendRequest.FromUserID,
|
||||
},
|
||||
)
|
||||
}
|
||||
if _, ok := existsMap[[...]string{friendRequest.FromUserID, friendRequest.ToUserID}]; !ok { // 好友 - 自己
|
||||
adds = append(adds, &relation.FriendModel{OwnerUserID: friendRequest.FromUserID, FriendUserID: friendRequest.ToUserID, AddSource: int32(constant.BecomeFriendByApply), OperatorUserID: friendRequest.FromUserID})
|
||||
adds = append(
|
||||
adds,
|
||||
&relation.FriendModel{
|
||||
OwnerUserID: friendRequest.FromUserID,
|
||||
FriendUserID: friendRequest.ToUserID,
|
||||
AddSource: int32(constant.BecomeFriendByApply),
|
||||
OperatorUserID: friendRequest.FromUserID,
|
||||
},
|
||||
)
|
||||
}
|
||||
if len(adds) > 0 {
|
||||
if err := f.friend.NewTx(tx).Create(ctx, adds); err != nil {
|
||||
@@ -216,27 +291,47 @@ func (f *friendDatabase) UpdateRemark(ctx context.Context, ownerUserID, friendUs
|
||||
}
|
||||
|
||||
// 获取ownerUserID的好友列表 无结果不返回错误
|
||||
func (f *friendDatabase) PageOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) {
|
||||
func (f *friendDatabase) PageOwnerFriends(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendModel, total int64, err error) {
|
||||
return f.friend.FindOwnerFriends(ctx, ownerUserID, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
// friendUserID在哪些人的好友列表中
|
||||
func (f *friendDatabase) PageInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) {
|
||||
func (f *friendDatabase) PageInWhoseFriends(
|
||||
ctx context.Context,
|
||||
friendUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendModel, total int64, err error) {
|
||||
return f.friend.FindInWhoseFriends(ctx, friendUserID, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
// 获取我发出去的好友申请 无结果不返回错误
|
||||
func (f *friendDatabase) PageFriendRequestFromMe(ctx context.Context, userID string, pageNumber, showNumber int32) (friends []*relation.FriendRequestModel, total int64, err error) {
|
||||
func (f *friendDatabase) PageFriendRequestFromMe(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendRequestModel, total int64, err error) {
|
||||
return f.friendRequest.FindFromUserID(ctx, userID, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
// 获取我收到的的好友申请 无结果不返回错误
|
||||
func (f *friendDatabase) PageFriendRequestToMe(ctx context.Context, userID string, pageNumber, showNumber int32) (friends []*relation.FriendRequestModel, total int64, err error) {
|
||||
func (f *friendDatabase) PageFriendRequestToMe(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendRequestModel, total int64, err error) {
|
||||
return f.friendRequest.FindToUserID(ctx, userID, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
// 获取某人指定好友的信息 如果有好友不存在,也返回错误
|
||||
func (f *friendDatabase) FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*relation.FriendModel, err error) {
|
||||
func (f *friendDatabase) FindFriendsWithError(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
friendUserIDs []string,
|
||||
) (friends []*relation.FriendModel, err error) {
|
||||
friends, err = f.friend.FindFriends(ctx, ownerUserID, friendUserIDs)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -247,6 +342,9 @@ func (f *friendDatabase) FindFriendsWithError(ctx context.Context, ownerUserID s
|
||||
return
|
||||
}
|
||||
|
||||
func (f *friendDatabase) FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) {
|
||||
func (f *friendDatabase) FindFriendUserIDs(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
) (friendUserIDs []string, err error) {
|
||||
return f.cache.GetFriendIDs(ctx, ownerUserID)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
@@ -12,10 +32,6 @@ import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/tx"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type GroupDatabase interface {
|
||||
@@ -23,33 +39,82 @@ type GroupDatabase interface {
|
||||
CreateGroup(ctx context.Context, groups []*relationTb.GroupModel, groupMembers []*relationTb.GroupMemberModel) error
|
||||
TakeGroup(ctx context.Context, groupID string) (group *relationTb.GroupModel, err error)
|
||||
FindGroup(ctx context.Context, groupIDs []string) (groups []*relationTb.GroupModel, err error)
|
||||
SearchGroup(ctx context.Context, keyword string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupModel, error)
|
||||
SearchGroup(
|
||||
ctx context.Context,
|
||||
keyword string,
|
||||
pageNumber, showNumber int32,
|
||||
) (uint32, []*relationTb.GroupModel, error)
|
||||
UpdateGroup(ctx context.Context, groupID string, data map[string]any) error
|
||||
DismissGroup(ctx context.Context, groupID string, deleteMember bool) error // 解散群,并删除群成员
|
||||
GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error)
|
||||
// GroupMember
|
||||
TakeGroupMember(ctx context.Context, groupID string, userID string) (groupMember *relationTb.GroupMemberModel, err error)
|
||||
TakeGroupMember(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
) (groupMember *relationTb.GroupMemberModel, err error)
|
||||
TakeGroupOwner(ctx context.Context, groupID string) (*relationTb.GroupMemberModel, error)
|
||||
FindGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) ([]*relationTb.GroupMemberModel, error)
|
||||
FindGroupMember(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
) ([]*relationTb.GroupMemberModel, error)
|
||||
FindGroupMemberUserID(ctx context.Context, groupID string) ([]string, error)
|
||||
FindGroupMemberNum(ctx context.Context, groupID string) (uint32, error)
|
||||
FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error)
|
||||
PageGroupRequest(ctx context.Context, groupIDs []string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupRequestModel, error)
|
||||
//PageGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (uint32, []*relationTb.GroupMemberModel, error)
|
||||
PageGetJoinGroup(ctx context.Context, userID string, pageNumber, showNumber int32) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error)
|
||||
PageGetGroupMember(ctx context.Context, groupID string, pageNumber, showNumber int32) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error)
|
||||
SearchGroupMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (uint32, []*relationTb.GroupMemberModel, error)
|
||||
HandlerGroupRequest(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32, member *relationTb.GroupMemberModel) error
|
||||
PageGroupRequest(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
pageNumber, showNumber int32,
|
||||
) (uint32, []*relationTb.GroupRequestModel, error)
|
||||
// PageGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber,
|
||||
// showNumber int32) (uint32, []*relationTb.GroupMemberModel, error)
|
||||
PageGetJoinGroup(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error)
|
||||
PageGetGroupMember(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error)
|
||||
SearchGroupMember(
|
||||
ctx context.Context,
|
||||
keyword string,
|
||||
groupIDs []string,
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
pageNumber, showNumber int32,
|
||||
) (uint32, []*relationTb.GroupMemberModel, error)
|
||||
HandlerGroupRequest(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
handledMsg string,
|
||||
handleResult int32,
|
||||
member *relationTb.GroupMemberModel,
|
||||
) error
|
||||
DeleteGroupMember(ctx context.Context, groupID string, userIDs []string) error
|
||||
MapGroupMemberUserID(ctx context.Context, groupIDs []string) (map[string]*relationTb.GroupSimpleUserID, error)
|
||||
MapGroupMemberNum(ctx context.Context, groupIDs []string) (map[string]uint32, error)
|
||||
TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string, roleLevel int32) error // 转让群
|
||||
TransferGroupOwner(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
oldOwnerUserID, newOwnerUserID string,
|
||||
roleLevel int32,
|
||||
) error // 转让群
|
||||
UpdateGroupMember(ctx context.Context, groupID string, userID string, data map[string]any) error
|
||||
UpdateGroupMembers(ctx context.Context, data []*relationTb.BatchUpdateGroupMember) error
|
||||
// GroupRequest
|
||||
CreateGroupRequest(ctx context.Context, requests []*relationTb.GroupRequestModel) error
|
||||
TakeGroupRequest(ctx context.Context, groupID string, userID string) (*relationTb.GroupRequestModel, error)
|
||||
PageGroupRequestUser(ctx context.Context, userID string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupRequestModel, error)
|
||||
PageGroupRequestUser(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (uint32, []*relationTb.GroupRequestModel, error)
|
||||
// SuperGroupModelInterface
|
||||
FindSuperGroup(ctx context.Context, groupIDs []string) ([]*unRelationTb.SuperGroupModel, error)
|
||||
FindJoinSuperGroup(ctx context.Context, userID string) ([]string, error)
|
||||
@@ -57,6 +122,11 @@ type GroupDatabase interface {
|
||||
DeleteSuperGroup(ctx context.Context, groupID string) error
|
||||
DeleteSuperGroupMember(ctx context.Context, groupID string, userIDs []string) error
|
||||
CreateSuperGroupMember(ctx context.Context, groupID string, userIDs []string) error
|
||||
|
||||
// 获取群总数
|
||||
CountTotal(ctx context.Context, before *time.Time) (count int64, err error)
|
||||
// 获取范围内群增量
|
||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||
}
|
||||
|
||||
func NewGroupDatabase(
|
||||
@@ -91,7 +161,14 @@ func InitGroupDatabase(db *gorm.DB, rdb redis.UniversalClient, database *mongo.D
|
||||
tx.NewGorm(db),
|
||||
tx.NewMongo(database.Client()),
|
||||
unrelation.NewSuperGroupMongoDriver(database),
|
||||
cache.NewGroupCacheRedis(rdb, relation.NewGroupDB(db), relation.NewGroupMemberDB(db), relation.NewGroupRequest(db), unrelation.NewSuperGroupMongoDriver(database), rcOptions),
|
||||
cache.NewGroupCacheRedis(
|
||||
rdb,
|
||||
relation.NewGroupDB(db),
|
||||
relation.NewGroupMemberDB(db),
|
||||
relation.NewGroupRequest(db),
|
||||
unrelation.NewSuperGroupMongoDriver(database),
|
||||
rcOptions,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -121,7 +198,11 @@ func (g *groupDatabase) FindGroupMemberNum(ctx context.Context, groupID string)
|
||||
return uint32(num), nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) CreateGroup(ctx context.Context, groups []*relationTb.GroupModel, groupMembers []*relationTb.GroupMemberModel) error {
|
||||
func (g *groupDatabase) CreateGroup(
|
||||
ctx context.Context,
|
||||
groups []*relationTb.GroupModel,
|
||||
groupMembers []*relationTb.GroupMemberModel,
|
||||
) error {
|
||||
var cache = g.cache.NewCache()
|
||||
if err := g.tx.Transaction(func(tx any) error {
|
||||
if len(groups) > 0 {
|
||||
@@ -162,7 +243,11 @@ func (g *groupDatabase) FindGroup(ctx context.Context, groupIDs []string) (group
|
||||
return g.cache.GetGroupsInfo(ctx, groupIDs)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) SearchGroup(ctx context.Context, keyword string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupModel, error) {
|
||||
func (g *groupDatabase) SearchGroup(
|
||||
ctx context.Context,
|
||||
keyword string,
|
||||
pageNumber, showNumber int32,
|
||||
) (uint32, []*relationTb.GroupModel, error) {
|
||||
return g.groupDB.Search(ctx, keyword, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
@@ -197,7 +282,11 @@ func (g *groupDatabase) DismissGroup(ctx context.Context, groupID string, delete
|
||||
return cache.ExecDel(ctx)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) TakeGroupMember(ctx context.Context, groupID string, userID string) (groupMember *relationTb.GroupMemberModel, err error) {
|
||||
func (g *groupDatabase) TakeGroupMember(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
) (groupMember *relationTb.GroupMemberModel, err error) {
|
||||
return g.cache.GetGroupMemberInfo(ctx, groupID, userID)
|
||||
}
|
||||
|
||||
@@ -209,11 +298,20 @@ func (g *groupDatabase) FindUserManagedGroupID(ctx context.Context, userID strin
|
||||
return g.groupMemberDB.FindUserManagedGroupID(ctx, userID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) PageGroupRequest(ctx context.Context, groupIDs []string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupRequestModel, error) {
|
||||
func (g *groupDatabase) PageGroupRequest(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
pageNumber, showNumber int32,
|
||||
) (uint32, []*relationTb.GroupRequestModel, error) {
|
||||
return g.groupRequestDB.PageGroup(ctx, groupIDs, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindGroupMember(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) (totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
func (g *groupDatabase) FindGroupMember(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
) (totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
if roleLevels == nil {
|
||||
for _, groupID := range groupIDs {
|
||||
groupMembers, err := g.cache.GetGroupMembersInfo(ctx, groupID, userIDs)
|
||||
@@ -227,7 +325,11 @@ func (g *groupDatabase) FindGroupMember(ctx context.Context, groupIDs []string,
|
||||
return g.groupMemberDB.Find(ctx, groupIDs, userIDs, roleLevels)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) PageGetJoinGroup(ctx context.Context, userID string, pageNumber, showNumber int32) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
func (g *groupDatabase) PageGetJoinGroup(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
groupIDs, err := g.cache.GetJoinedGroupIDs(ctx, userID)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
@@ -242,7 +344,11 @@ func (g *groupDatabase) PageGetJoinGroup(ctx context.Context, userID string, pag
|
||||
return uint32(len(groupIDs)), totalGroupMembers, nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) PageGetGroupMember(ctx context.Context, groupID string, pageNumber, showNumber int32) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
func (g *groupDatabase) PageGetGroupMember(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
groupMemberIDs, err := g.cache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
@@ -258,11 +364,25 @@ func (g *groupDatabase) PageGetGroupMember(ctx context.Context, groupID string,
|
||||
return uint32(len(groupMemberIDs)), members, nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) SearchGroupMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (uint32, []*relationTb.GroupMemberModel, error) {
|
||||
func (g *groupDatabase) SearchGroupMember(
|
||||
ctx context.Context,
|
||||
keyword string,
|
||||
groupIDs []string,
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
pageNumber, showNumber int32,
|
||||
) (uint32, []*relationTb.GroupMemberModel, error) {
|
||||
return g.groupMemberDB.SearchMember(ctx, keyword, groupIDs, userIDs, roleLevels, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) HandlerGroupRequest(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32, member *relationTb.GroupMemberModel) error {
|
||||
func (g *groupDatabase) HandlerGroupRequest(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
handledMsg string,
|
||||
handleResult int32,
|
||||
member *relationTb.GroupMemberModel,
|
||||
) error {
|
||||
cache := g.cache.NewCache()
|
||||
if err := g.tx.Transaction(func(tx any) error {
|
||||
if err := g.groupRequestDB.NewTx(tx).UpdateHandler(ctx, groupID, userID, handledMsg, handleResult); err != nil {
|
||||
@@ -285,10 +405,18 @@ func (g *groupDatabase) DeleteGroupMember(ctx context.Context, groupID string, u
|
||||
if err := g.groupMemberDB.Delete(ctx, groupID, userIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
return g.cache.DelGroupMembersHash(groupID).DelGroupMemberIDs(groupID).DelGroupsMemberNum(groupID).DelJoinedGroupID(userIDs...).DelGroupMembersInfo(groupID, userIDs...).ExecDel(ctx)
|
||||
return g.cache.DelGroupMembersHash(groupID).
|
||||
DelGroupMemberIDs(groupID).
|
||||
DelGroupsMemberNum(groupID).
|
||||
DelJoinedGroupID(userIDs...).
|
||||
DelGroupMembersInfo(groupID, userIDs...).
|
||||
ExecDel(ctx)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) MapGroupMemberUserID(ctx context.Context, groupIDs []string) (map[string]*relationTb.GroupSimpleUserID, error) {
|
||||
func (g *groupDatabase) MapGroupMemberUserID(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
) (map[string]*relationTb.GroupSimpleUserID, error) {
|
||||
return g.cache.GetGroupMemberHashMap(ctx, groupIDs)
|
||||
}
|
||||
|
||||
@@ -304,7 +432,12 @@ func (g *groupDatabase) MapGroupMemberNum(ctx context.Context, groupIDs []string
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string, roleLevel int32) error {
|
||||
func (g *groupDatabase) TransferGroupOwner(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
oldOwnerUserID, newOwnerUserID string,
|
||||
roleLevel int32,
|
||||
) error {
|
||||
if err := g.tx.Transaction(func(tx any) error {
|
||||
rowsAffected, err := g.groupMemberDB.NewTx(tx).UpdateRoleLevel(ctx, groupID, oldOwnerUserID, roleLevel)
|
||||
if err != nil {
|
||||
@@ -327,7 +460,12 @@ func (g *groupDatabase) TransferGroupOwner(ctx context.Context, groupID string,
|
||||
return g.cache.DelGroupMembersInfo(groupID, oldOwnerUserID, newOwnerUserID).ExecDel(ctx)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) UpdateGroupMember(ctx context.Context, groupID string, userID string, data map[string]any) error {
|
||||
func (g *groupDatabase) UpdateGroupMember(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
data map[string]any,
|
||||
) error {
|
||||
if err := g.groupMemberDB.Update(ctx, groupID, userID, data); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -362,15 +500,26 @@ func (g *groupDatabase) CreateGroupRequest(ctx context.Context, requests []*rela
|
||||
})
|
||||
}
|
||||
|
||||
func (g *groupDatabase) TakeGroupRequest(ctx context.Context, groupID string, userID string) (*relationTb.GroupRequestModel, error) {
|
||||
func (g *groupDatabase) TakeGroupRequest(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
) (*relationTb.GroupRequestModel, error) {
|
||||
return g.groupRequestDB.Take(ctx, groupID, userID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) PageGroupRequestUser(ctx context.Context, userID string, pageNumber, showNumber int32) (uint32, []*relationTb.GroupRequestModel, error) {
|
||||
func (g *groupDatabase) PageGroupRequestUser(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (uint32, []*relationTb.GroupRequestModel, error) {
|
||||
return g.groupRequestDB.Page(ctx, userID, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindSuperGroup(ctx context.Context, groupIDs []string) (models []*unRelationTb.SuperGroupModel, err error) {
|
||||
func (g *groupDatabase) FindSuperGroup(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
) (models []*unRelationTb.SuperGroupModel, err error) {
|
||||
return g.cache.GetSuperGroupMemberIDs(ctx, groupIDs...)
|
||||
}
|
||||
|
||||
@@ -419,3 +568,11 @@ func (g *groupDatabase) CreateSuperGroupMember(ctx context.Context, groupID stri
|
||||
}
|
||||
return g.cache.DelSuperGroupMemberIDs(groupID).DelJoinedSuperGroupIDs(userIDs...).ExecDel(ctx)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
||||
return g.groupDB.CountTotal(ctx, before)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
|
||||
return g.groupDB.CountRangeEverydayTotal(ctx, start, end)
|
||||
}
|
||||
|
||||
+68
-123
@@ -1,10 +1,10 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
@@ -48,6 +48,9 @@ type CommonMsgDatabase interface {
|
||||
GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error)
|
||||
// 删除会话消息重置最小seq, remainTime为消息保留的时间单位秒,超时消息删除, 传0删除所有消息(此方法不删除redis cache)
|
||||
DeleteConversationMsgsAndSetMinSeq(ctx context.Context, conversationID string, remainTime int64) error
|
||||
// 用户标记删除过期消息返回标记删除的seq列表
|
||||
UserMsgsDestruct(cte context.Context, userID string, conversationID string, destructTime int64, lastMsgDestructTime time.Time) (seqs []int64, err error)
|
||||
|
||||
// 用户根据seq删除消息
|
||||
DeleteUserMsgsBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) error
|
||||
// 物理删除消息置空
|
||||
@@ -71,7 +74,7 @@ type CommonMsgDatabase interface {
|
||||
GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error)
|
||||
UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error
|
||||
|
||||
GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (maxSeq, minSeq int64, err error)
|
||||
GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo int64, err error)
|
||||
GetConversationMinMaxSeqInMongoAndCache(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error)
|
||||
SetSendMsgStatus(ctx context.Context, id string, status int32) error
|
||||
GetSendMsgStatus(ctx context.Context, id string) (int32, error)
|
||||
@@ -82,26 +85,17 @@ type CommonMsgDatabase interface {
|
||||
MsgToPushMQ(ctx context.Context, key, conversarionID string, msg2mq *sdkws.MsgData) (int32, int64, error)
|
||||
MsgToMongoMQ(ctx context.Context, key, conversarionID string, msgs []*sdkws.MsgData, lastSeq int64) error
|
||||
|
||||
// modify
|
||||
JudgeMessageReactionExist(ctx context.Context, clientMsgID string, sessionType int32) (bool, error)
|
||||
SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error
|
||||
SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error)
|
||||
GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (*pbMsg.ExtendMsg, error)
|
||||
InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*sdkws.KeyValue) error
|
||||
GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error)
|
||||
GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error)
|
||||
DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error
|
||||
DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*sdkws.KeyValue) error
|
||||
RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*unRelationTb.UserCount, dateCount map[string]int64, err error)
|
||||
RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*unRelationTb.GroupCount, dateCount map[string]int64, err error)
|
||||
}
|
||||
|
||||
func NewCommonMsgDatabase(msgDocModel unRelationTb.MsgDocModelInterface, cacheModel cache.MsgModel) CommonMsgDatabase {
|
||||
return &commonMsgDatabase{
|
||||
msgDocDatabase: msgDocModel,
|
||||
cache: cacheModel,
|
||||
producer: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic),
|
||||
producerToMongo: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic),
|
||||
producerToPush: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic),
|
||||
producerToModify: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToModify.Topic),
|
||||
msgDocDatabase: msgDocModel,
|
||||
cache: cacheModel,
|
||||
producer: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic),
|
||||
producerToMongo: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic),
|
||||
producerToPush: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,15 +107,13 @@ func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database)
|
||||
}
|
||||
|
||||
type commonMsgDatabase struct {
|
||||
msgDocDatabase unRelationTb.MsgDocModelInterface
|
||||
extendMsgDatabase unRelationTb.ExtendMsgSetModelInterface
|
||||
extendMsgSetModel unRelationTb.ExtendMsgSetModel
|
||||
msg unRelationTb.MsgDocModel
|
||||
cache cache.MsgModel
|
||||
producer *kafka.Producer
|
||||
producerToMongo *kafka.Producer
|
||||
producerToModify *kafka.Producer
|
||||
producerToPush *kafka.Producer
|
||||
msgDocDatabase unRelationTb.MsgDocModelInterface
|
||||
msg unRelationTb.MsgDocModel
|
||||
cache cache.MsgModel
|
||||
producer *kafka.Producer
|
||||
producerToMongo *kafka.Producer
|
||||
producerToModify *kafka.Producer
|
||||
producerToPush *kafka.Producer
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) MsgToMQ(ctx context.Context, key string, msg2mq *sdkws.MsgData) error {
|
||||
@@ -389,44 +381,6 @@ func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversat
|
||||
return totalMsgs, nil
|
||||
}
|
||||
|
||||
// func (db *commonMsgDatabase) refetchDelSeqsMsgs(ctx context.Context, conversationID string, delNums, rangeBegin, begin int64) (seqMsgs []*unRelationTb.MsgDataModel, err error) {
|
||||
// var reFetchSeqs []int64
|
||||
// if delNums > 0 {
|
||||
// newBeginSeq := rangeBegin - delNums
|
||||
// if newBeginSeq >= begin {
|
||||
// newEndSeq := rangeBegin - 1
|
||||
// for i := newBeginSeq; i <= newEndSeq; i++ {
|
||||
// reFetchSeqs = append(reFetchSeqs, i)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if len(reFetchSeqs) == 0 {
|
||||
// return
|
||||
// }
|
||||
// if len(reFetchSeqs) > 0 {
|
||||
// m := db.msg.GetDocIDSeqsMap(conversationID, reFetchSeqs)
|
||||
// for docID, seqs := range m {
|
||||
// msgs, _, err := db.findMsgInfoBySeq(ctx, docID, seqs)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// for _, msg := range msgs {
|
||||
// if msg.Status != constant.MsgDeleted {
|
||||
// seqMsgs = append(seqMsgs, msg)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if len(seqMsgs) < int(delNums) {
|
||||
// seqMsgs2, err := db.refetchDelSeqsMsgs(ctx, conversationID, delNums-int64(len(seqMsgs)), rangeBegin-1, begin)
|
||||
// if err != nil {
|
||||
// return seqMsgs, err
|
||||
// }
|
||||
// seqMsgs = append(seqMsgs, seqMsgs2...)
|
||||
// }
|
||||
// return seqMsgs, nil
|
||||
// }
|
||||
|
||||
func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID string, seqs []int64) (totalMsgs []*unRelationTb.MsgInfoModel, err error) {
|
||||
msgs, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, docID, seqs)
|
||||
for _, msg := range msgs {
|
||||
@@ -634,6 +588,49 @@ func (db *commonMsgDatabase) DeleteConversationMsgsAndSetMinSeq(ctx context.Cont
|
||||
return db.cache.SetMinSeq(ctx, conversationID, minSeq)
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) UserMsgsDestruct(ctx context.Context, userID string, conversationID string, destructTime int64, lastMsgDestructTime time.Time) (seqs []int64, err error) {
|
||||
var index int64
|
||||
for {
|
||||
// from oldest 2 newest
|
||||
msgDocModel, err := db.msgDocDatabase.GetMsgDocModelByIndex(ctx, conversationID, index, 1)
|
||||
if err != nil || msgDocModel.DocID == "" {
|
||||
if err != nil {
|
||||
if err == unrelation.ErrMsgListNotExist {
|
||||
log.ZDebug(ctx, "deleteMsgRecursion finished", "conversationID", conversationID, "userID", userID, "index", index)
|
||||
} else {
|
||||
log.ZError(ctx, "deleteMsgRecursion GetUserMsgListByIndex failed", err, "conversationID", conversationID, "index", index)
|
||||
}
|
||||
}
|
||||
// 获取报错,或者获取不到了,物理删除并且返回seq delMongoMsgsPhysical(delStruct.delDocIDList), 结束递归
|
||||
break
|
||||
}
|
||||
index++
|
||||
//&& msgDocModel.Msg[0].Msg.SendTime > lastMsgDestructTime.UnixMilli()
|
||||
if len(msgDocModel.Msg) > 0 {
|
||||
for _, msg := range msgDocModel.Msg {
|
||||
if msg != nil && msg.Msg != nil && msg.Msg.SendTime+destructTime*1000 <= time.Now().UnixMilli() {
|
||||
if msg.Msg.SendTime > lastMsgDestructTime.UnixMilli() && !utils.Contain(userID, msg.DelList...) {
|
||||
seqs = append(seqs, msg.Msg.Seq)
|
||||
}
|
||||
} else {
|
||||
log.ZDebug(ctx, "deleteMsgRecursion finished", "conversationID", conversationID, "userID", userID, "index", index)
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "UserMsgsDestruct", "conversationID", conversationID, "userID", userID, "seqs", seqs)
|
||||
if len(seqs) > 0 {
|
||||
latestSeq := seqs[len(seqs)-1]
|
||||
if err := db.cache.SetConversationUserMinSeq(ctx, conversationID, userID, latestSeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return seqs, nil
|
||||
}
|
||||
|
||||
// this is struct for recursion
|
||||
type delMsgRecursionStruct struct {
|
||||
minSeq int64
|
||||
@@ -848,7 +845,7 @@ func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache(ctx context
|
||||
return
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (maxSeq, minSeq int64, err error) {
|
||||
func (db *commonMsgDatabase) GetMongoMaxAndMinSeq(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo int64, err error) {
|
||||
return db.GetMinMaxSeqMongo(ctx, conversationID)
|
||||
}
|
||||
|
||||
@@ -866,62 +863,10 @@ func (db *commonMsgDatabase) GetMinMaxSeqMongo(ctx context.Context, conversation
|
||||
return
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) JudgeMessageReactionExist(ctx context.Context, clientMsgID string, sessionType int32) (bool, error) {
|
||||
return db.cache.JudgeMessageReactionExist(ctx, clientMsgID, sessionType)
|
||||
func (db *commonMsgDatabase) RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*unRelationTb.UserCount, dateCount map[string]int64, err error) {
|
||||
return db.msgDocDatabase.RangeUserSendCount(ctx, start, end, group, ase, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error {
|
||||
return db.cache.SetMessageTypeKeyValue(ctx, clientMsgID, sessionType, typeKey, value)
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error) {
|
||||
return db.cache.SetMessageReactionExpire(ctx, clientMsgID, sessionType, expiration)
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error) {
|
||||
return db.cache.GetMessageTypeKeyValue(ctx, clientMsgID, sessionType, typeKey)
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error) {
|
||||
return db.cache.GetOneMessageAllReactionList(ctx, clientMsgID, sessionType)
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error {
|
||||
return db.cache.DeleteOneMessageKey(ctx, clientMsgID, sessionType, subKey)
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensions map[string]*sdkws.KeyValue) error {
|
||||
return db.extendMsgDatabase.InsertOrUpdateReactionExtendMsgSet(ctx, conversationID, sessionType, clientMsgID, msgFirstModifyTime, db.extendMsgSetModel.Pb2Model(reactionExtensions))
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) GetExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (*pbMsg.ExtendMsg, error) {
|
||||
extendMsgSet, err := db.extendMsgDatabase.GetExtendMsgSet(ctx, conversationID, sessionType, maxMsgUpdateTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extendMsg, ok := extendMsgSet.ExtendMsgs[clientMsgID]
|
||||
if !ok {
|
||||
return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("cant find client msg id: %s", clientMsgID))
|
||||
}
|
||||
reactionExtensionList := make(map[string]*pbMsg.KeyValueResp)
|
||||
for key, model := range extendMsg.ReactionExtensionList {
|
||||
reactionExtensionList[key] = &pbMsg.KeyValueResp{
|
||||
KeyValue: &sdkws.KeyValue{
|
||||
TypeKey: model.TypeKey,
|
||||
Value: model.Value,
|
||||
LatestUpdateTime: model.LatestUpdateTime,
|
||||
},
|
||||
}
|
||||
}
|
||||
return &pbMsg.ExtendMsg{
|
||||
ReactionExtensions: reactionExtensionList,
|
||||
ClientMsgID: extendMsg.ClientMsgID,
|
||||
MsgFirstModifyTime: extendMsg.MsgFirstModifyTime,
|
||||
AttachedInfo: extendMsg.AttachedInfo,
|
||||
Ex: extendMsg.Ex,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensions map[string]*sdkws.KeyValue) error {
|
||||
return db.extendMsgDatabase.DeleteReactionExtendMsgSet(ctx, conversationID, sessionType, clientMsgID, msgFirstModifyTime, db.extendMsgSetModel.Pb2Model(reactionExtensions))
|
||||
func (db *commonMsgDatabase) RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*unRelationTb.GroupCount, dateCount map[string]int64, err error) {
|
||||
return db.msgDocDatabase.RangeGroupSendCount(ctx, start, end, ase, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
@@ -11,10 +25,11 @@ import (
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
func Test_BatchInsertChat2DB(t *testing.T) {
|
||||
@@ -193,7 +208,9 @@ func Test_FindBySeq(t *testing.T) {
|
||||
}
|
||||
db := GetDB()
|
||||
ctx := context.Background()
|
||||
fmt.Println(db.msgDocDatabase.(*unrelation.MsgMongoDriver).GetMsgBySeqIndexIn1Doc(ctx, "100", "si_100_101:0", []int64{1}))
|
||||
fmt.Println(
|
||||
db.msgDocDatabase.(*unrelation.MsgMongoDriver).GetMsgBySeqIndexIn1Doc(ctx, "100", "si_100_101:0", []int64{1}),
|
||||
)
|
||||
//res, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, "123456", "test:0", []int64{1, 2, 3})
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3/cont"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type S3Database interface {
|
||||
PartLimit() *s3.PartLimit
|
||||
PartSize(ctx context.Context, size int64) (int64, error)
|
||||
AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error)
|
||||
InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error)
|
||||
CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error)
|
||||
AccessURL(ctx context.Context, name string, expire time.Duration) (time.Time, string, error)
|
||||
SetObject(ctx context.Context, info *relation.ObjectModel) error
|
||||
}
|
||||
|
||||
func NewS3Database(s3 s3.Interface, obj relation.ObjectInfoModelInterface) S3Database {
|
||||
return &s3Database{
|
||||
s3: cont.New(s3),
|
||||
obj: obj,
|
||||
}
|
||||
}
|
||||
|
||||
type s3Database struct {
|
||||
s3 *cont.Controller
|
||||
obj relation.ObjectInfoModelInterface
|
||||
}
|
||||
|
||||
func (s *s3Database) PartSize(ctx context.Context, size int64) (int64, error) {
|
||||
return s.s3.PartSize(ctx, size)
|
||||
}
|
||||
|
||||
func (s *s3Database) PartLimit() *s3.PartLimit {
|
||||
return s.s3.PartLimit()
|
||||
}
|
||||
|
||||
func (s *s3Database) AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) {
|
||||
return s.s3.AuthSign(ctx, uploadID, partNumbers)
|
||||
}
|
||||
|
||||
func (s *s3Database) InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error) {
|
||||
return s.s3.InitiateUpload(ctx, hash, size, expire, maxParts)
|
||||
}
|
||||
|
||||
func (s *s3Database) CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error) {
|
||||
return s.s3.CompleteUpload(ctx, uploadID, parts)
|
||||
}
|
||||
|
||||
func (s *s3Database) SetObject(ctx context.Context, info *relation.ObjectModel) error {
|
||||
return s.obj.SetObject(ctx, info)
|
||||
}
|
||||
|
||||
func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Duration) (time.Time, string, error) {
|
||||
obj, err := s.obj.Take(ctx, name)
|
||||
if err != nil {
|
||||
return time.Time{}, "", err
|
||||
}
|
||||
opt := &s3.AccessURLOption{
|
||||
ContentType: obj.ContentType,
|
||||
}
|
||||
if filename := filepath.Base(obj.Name); filename != "" {
|
||||
opt.ContentDisposition = `attachment; filename=` + filename
|
||||
}
|
||||
expireTime := time.Now().Add(expire)
|
||||
rawURL, err := s.s3.AccessURL(ctx, obj.Key, expire, opt)
|
||||
if err != nil {
|
||||
return time.Time{}, "", err
|
||||
}
|
||||
return expireTime, rawURL, nil
|
||||
}
|
||||
@@ -1,538 +0,0 @@
|
||||
package controller
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/obj"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/third"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/google/uuid"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
hashPrefix = "hash"
|
||||
tempPrefix = "temp"
|
||||
fragmentPrefix = "fragment_"
|
||||
urlsName = "urls.json"
|
||||
)
|
||||
|
||||
type S3Database interface {
|
||||
ApplyPut(ctx context.Context, req *third.ApplyPutReq) (*third.ApplyPutResp, error)
|
||||
GetPut(ctx context.Context, req *third.GetPutReq) (*third.GetPutResp, error)
|
||||
ConfirmPut(ctx context.Context, req *third.ConfirmPutReq) (*third.ConfirmPutResp, error)
|
||||
GetUrl(ctx context.Context, req *third.GetUrlReq) (*third.GetUrlResp, error)
|
||||
GetHashInfo(ctx context.Context, req *third.GetHashInfoReq) (*third.GetHashInfoResp, error)
|
||||
CleanExpirationObject(ctx context.Context, t time.Time)
|
||||
}
|
||||
|
||||
func NewS3Database(obj obj.Interface, hash relation.ObjectHashModelInterface, info relation.ObjectInfoModelInterface, put relation.ObjectPutModelInterface, url *url.URL) S3Database {
|
||||
return &s3Database{
|
||||
url: url,
|
||||
obj: obj,
|
||||
hash: hash,
|
||||
info: info,
|
||||
put: put,
|
||||
}
|
||||
}
|
||||
|
||||
type s3Database struct {
|
||||
url *url.URL
|
||||
obj obj.Interface
|
||||
hash relation.ObjectHashModelInterface
|
||||
info relation.ObjectInfoModelInterface
|
||||
put relation.ObjectPutModelInterface
|
||||
}
|
||||
|
||||
// today 今天的日期
|
||||
func (c *s3Database) today() string {
|
||||
return time.Now().Format("20060102")
|
||||
}
|
||||
|
||||
// fragmentName 根据序号生成文件名
|
||||
func (c *s3Database) fragmentName(index int) string {
|
||||
return fragmentPrefix + strconv.Itoa(index+1)
|
||||
}
|
||||
|
||||
// getFragmentNum 获取分片大小和分片数量
|
||||
func (c *s3Database) getFragmentNum(fragmentSize int64, objectSize int64) (int64, int) {
|
||||
if size := c.obj.MinFragmentSize(); fragmentSize < size {
|
||||
fragmentSize = size
|
||||
}
|
||||
if fragmentSize <= 0 || objectSize <= fragmentSize {
|
||||
return objectSize, 1
|
||||
} else {
|
||||
num := int(objectSize / fragmentSize)
|
||||
if objectSize%fragmentSize > 0 {
|
||||
num++
|
||||
}
|
||||
if n := c.obj.MaxFragmentNum(); num > n {
|
||||
num = n
|
||||
}
|
||||
return fragmentSize, num
|
||||
}
|
||||
}
|
||||
|
||||
func (c *s3Database) CheckHash(hash string) error {
|
||||
val, err := hex.DecodeString(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(val) != md5.Size {
|
||||
return errs.ErrArgs.Wrap("invalid hash")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *s3Database) urlName(name string) string {
|
||||
u := url.URL{
|
||||
Scheme: c.url.Scheme,
|
||||
Opaque: c.url.Opaque,
|
||||
User: c.url.User,
|
||||
Host: c.url.Host,
|
||||
Path: c.url.Path,
|
||||
RawPath: c.url.RawPath,
|
||||
ForceQuery: c.url.ForceQuery,
|
||||
RawQuery: c.url.RawQuery,
|
||||
Fragment: c.url.Fragment,
|
||||
RawFragment: c.url.RawFragment,
|
||||
}
|
||||
v := make(url.Values, 1)
|
||||
v.Set("name", name)
|
||||
u.RawQuery = v.Encode()
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (c *s3Database) UUID() string {
|
||||
return uuid.New().String()
|
||||
}
|
||||
|
||||
func (c *s3Database) HashName(hash string) string {
|
||||
return path.Join(hashPrefix, hash+"_"+c.today()+"_"+c.UUID())
|
||||
}
|
||||
|
||||
func (c *s3Database) isNotFound(err error) bool {
|
||||
return relation.IsNotFound(err)
|
||||
}
|
||||
|
||||
func (c *s3Database) ApplyPut(ctx context.Context, req *third.ApplyPutReq) (*third.ApplyPutResp, error) {
|
||||
if err := c.CheckHash(req.Hash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.obj.CheckName(req.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.ValidTime != 0 && req.ValidTime <= time.Now().UnixMilli() {
|
||||
return nil, errors.New("invalid ValidTime")
|
||||
}
|
||||
var expirationTime *time.Time
|
||||
if req.ValidTime != 0 {
|
||||
expirationTime = utils.ToPtr(time.UnixMilli(req.ValidTime))
|
||||
}
|
||||
if hash, err := c.hash.Take(ctx, req.Hash, c.obj.Name()); err == nil {
|
||||
o := relation.ObjectInfoModel{
|
||||
Name: req.Name,
|
||||
Hash: hash.Hash,
|
||||
ValidTime: expirationTime,
|
||||
ContentType: req.ContentType,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
if err := c.info.SetObject(ctx, &o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &third.ApplyPutResp{Url: c.urlName(o.Name)}, nil // 服务器已存在
|
||||
} else if !c.isNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
// 新上传
|
||||
var fragmentNum int
|
||||
const effective = time.Hour * 24 * 2
|
||||
req.FragmentSize, fragmentNum = c.getFragmentNum(req.FragmentSize, req.Size)
|
||||
put := relation.ObjectPutModel{
|
||||
PutID: req.PutID,
|
||||
Hash: req.Hash,
|
||||
Name: req.Name,
|
||||
ObjectSize: req.Size,
|
||||
ContentType: req.ContentType,
|
||||
FragmentSize: req.FragmentSize,
|
||||
ValidTime: expirationTime,
|
||||
EffectiveTime: time.Now().Add(effective),
|
||||
}
|
||||
if put.PutID == "" {
|
||||
put.PutID = c.UUID()
|
||||
}
|
||||
if v, err := c.put.Take(ctx, put.PutID); err == nil {
|
||||
now := time.Now().UnixMilli()
|
||||
if v.EffectiveTime.UnixMilli() <= now {
|
||||
if err := c.put.DelPut(ctx, []string{v.PutID}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, errs.ErrDuplicateKey.Wrap(fmt.Sprintf("duplicate put id %s", put.PutID))
|
||||
}
|
||||
} else if !c.isNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
put.Path = path.Join(tempPrefix, c.today(), req.Hash, put.PutID)
|
||||
putURLs := make([]string, 0, fragmentNum)
|
||||
for i := 0; i < fragmentNum; i++ {
|
||||
url, err := c.obj.PresignedPutURL(ctx, &obj.ApplyPutArgs{
|
||||
Bucket: c.obj.TempBucket(),
|
||||
Name: path.Join(put.Path, c.fragmentName(i)),
|
||||
Effective: effective,
|
||||
MaxObjectSize: req.FragmentSize,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
putURLs = append(putURLs, url)
|
||||
}
|
||||
urlsJsonData, err := json.Marshal(putURLs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := md5.Sum(urlsJsonData)
|
||||
put.PutURLsHash = hex.EncodeToString(t[:])
|
||||
_, err = c.obj.PutObject(ctx, &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: path.Join(put.Path, urlsName)}, bytes.NewReader(urlsJsonData), int64(len(urlsJsonData)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
put.CreateTime = time.Now()
|
||||
if err := c.put.Create(ctx, []*relation.ObjectPutModel{&put}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &third.ApplyPutResp{
|
||||
PutID: put.PutID,
|
||||
FragmentSize: put.FragmentSize,
|
||||
PutURLs: putURLs,
|
||||
ValidTime: put.EffectiveTime.UnixMilli(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *s3Database) GetPut(ctx context.Context, req *third.GetPutReq) (*third.GetPutResp, error) {
|
||||
up, err := c.put.Take(ctx, req.PutID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader, err := c.obj.GetObject(ctx, &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: path.Join(up.Path, urlsName)})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
urlsData, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := md5.Sum(urlsData)
|
||||
if h := hex.EncodeToString(t[:]); h != up.PutURLsHash {
|
||||
return nil, fmt.Errorf("invalid put urls hash %s %s", h, up.PutURLsHash)
|
||||
}
|
||||
var urls []string
|
||||
if err := json.Unmarshal(urlsData, &urls); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, fragmentNum := c.getFragmentNum(up.FragmentSize, up.ObjectSize)
|
||||
if len(urls) != fragmentNum {
|
||||
return nil, fmt.Errorf("invalid urls length %d fragment %d", len(urls), fragmentNum)
|
||||
}
|
||||
fragments := make([]*third.GetPutFragment, fragmentNum)
|
||||
for i := 0; i < fragmentNum; i++ {
|
||||
name := path.Join(up.Path, c.fragmentName(i))
|
||||
o, err := c.obj.GetObjectInfo(ctx, &obj.BucketObject{
|
||||
Bucket: c.obj.TempBucket(),
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
if c.obj.IsNotFound(err) {
|
||||
fragments[i] = &third.GetPutFragment{Url: urls[i]}
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
fragments[i] = &third.GetPutFragment{Size: o.Size, Hash: o.Hash, Url: urls[i]}
|
||||
}
|
||||
var validTime int64
|
||||
if up.ValidTime != nil {
|
||||
validTime = up.ValidTime.UnixMilli()
|
||||
}
|
||||
return &third.GetPutResp{
|
||||
FragmentSize: up.FragmentSize,
|
||||
Size: up.ObjectSize,
|
||||
Name: up.Name,
|
||||
Hash: up.Hash,
|
||||
Fragments: fragments,
|
||||
PutURLsHash: up.PutURLsHash,
|
||||
ContentType: up.ContentType,
|
||||
ValidTime: validTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *s3Database) ConfirmPut(ctx context.Context, req *third.ConfirmPutReq) (_ *third.ConfirmPutResp, _err error) {
|
||||
put, err := c.put.Take(ctx, req.PutID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, pack := c.getFragmentNum(put.FragmentSize, put.ObjectSize)
|
||||
defer func() {
|
||||
if _err == nil {
|
||||
// 清理上传的碎片
|
||||
err := c.obj.DeleteObject(ctx, &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: put.Path})
|
||||
if err != nil {
|
||||
log.ZError(ctx, "deleteObject failed", err, "Bucket", c.obj.TempBucket(), "Path", put.Path)
|
||||
}
|
||||
}
|
||||
}()
|
||||
now := time.Now().UnixMilli()
|
||||
if put.EffectiveTime.UnixMilli() < now {
|
||||
return nil, errs.ErrFileUploadedExpired.Wrap("put expired")
|
||||
}
|
||||
if put.ValidTime != nil && put.ValidTime.UnixMilli() < now {
|
||||
return nil, errs.ErrFileUploadedExpired.Wrap("object expired")
|
||||
}
|
||||
if hash, err := c.hash.Take(ctx, put.Hash, c.obj.Name()); err == nil {
|
||||
o := relation.ObjectInfoModel{
|
||||
Name: put.Name,
|
||||
Hash: hash.Hash,
|
||||
ValidTime: put.ValidTime,
|
||||
ContentType: put.ContentType,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
if err := c.info.SetObject(ctx, &o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err := c.obj.DeleteObject(ctx, &obj.BucketObject{
|
||||
Bucket: c.obj.TempBucket(),
|
||||
Name: put.Path,
|
||||
})
|
||||
if err != nil {
|
||||
log.ZError(ctx, "DeleteObject", err, "Bucket", c.obj.TempBucket(), "Path", put.Path)
|
||||
}
|
||||
}()
|
||||
// 服务端已存在
|
||||
return &third.ConfirmPutResp{
|
||||
Url: c.urlName(o.Name),
|
||||
}, nil
|
||||
} else if !c.isNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
src := make([]obj.BucketObject, pack)
|
||||
for i := 0; i < pack; i++ {
|
||||
name := path.Join(put.Path, c.fragmentName(i))
|
||||
o, err := c.obj.GetObjectInfo(ctx, &obj.BucketObject{
|
||||
Bucket: c.obj.TempBucket(),
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i+1 == pack { // 最后一个
|
||||
size := put.ObjectSize - put.FragmentSize*int64(i)
|
||||
if size != o.Size {
|
||||
return nil, fmt.Errorf("last fragment %d size %d not equal to %d hash %s", i, o.Size, size, o.Hash)
|
||||
}
|
||||
} else {
|
||||
if o.Size != put.FragmentSize {
|
||||
return nil, fmt.Errorf("fragment %d size %d not equal to %d hash %s", i, o.Size, put.FragmentSize, o.Hash)
|
||||
}
|
||||
}
|
||||
src[i] = obj.BucketObject{
|
||||
Bucket: c.obj.TempBucket(),
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
dst := &obj.BucketObject{
|
||||
Bucket: c.obj.DataBucket(),
|
||||
Name: c.HashName(put.Hash),
|
||||
}
|
||||
if len(src) == 1 { // 未分片直接触发copy
|
||||
// 检查数据完整性,避免脏数据
|
||||
o, err := c.obj.GetObjectInfo(ctx, &src[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if put.ObjectSize != o.Size {
|
||||
return nil, fmt.Errorf("size mismatching should %d reality %d", put.ObjectSize, o.Size)
|
||||
}
|
||||
if put.Hash != o.Hash {
|
||||
return nil, fmt.Errorf("hash mismatching should %s reality %s", put.Hash, o.Hash)
|
||||
}
|
||||
if err := c.obj.CopyObject(ctx, &src[0], dst); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
tempBucket := &obj.BucketObject{
|
||||
Bucket: c.obj.TempBucket(),
|
||||
Name: path.Join(put.Path, "merge_"+c.UUID()),
|
||||
}
|
||||
defer func() { // 清理合成的文件
|
||||
if err := c.obj.DeleteObject(ctx, tempBucket); err != nil {
|
||||
log.ZError(ctx, "DeleteObject", err, "Bucket", tempBucket.Bucket, "Path", tempBucket.Name)
|
||||
}
|
||||
}()
|
||||
err := c.obj.ComposeObject(ctx, src, tempBucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, err := c.obj.GetObjectInfo(ctx, tempBucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if put.ObjectSize != info.Size {
|
||||
return nil, fmt.Errorf("size mismatch should %d reality %d", put.ObjectSize, info.Size)
|
||||
}
|
||||
if put.Hash != info.Hash {
|
||||
return nil, fmt.Errorf("hash mismatch should %s reality %s", put.Hash, info.Hash)
|
||||
}
|
||||
if err := c.obj.CopyObject(ctx, tempBucket, dst); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
h := &relation.ObjectHashModel{
|
||||
Hash: put.Hash,
|
||||
Engine: c.obj.Name(),
|
||||
Size: put.ObjectSize,
|
||||
Bucket: c.obj.DataBucket(),
|
||||
Name: dst.Name,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
if err := c.hash.Create(ctx, []*relation.ObjectHashModel{h}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o := &relation.ObjectInfoModel{
|
||||
Name: put.Name,
|
||||
Hash: put.Hash,
|
||||
ContentType: put.ContentType,
|
||||
ValidTime: put.ValidTime,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
if err := c.info.SetObject(ctx, o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.put.DelPut(ctx, []string{put.PutID}); err != nil {
|
||||
log.ZError(ctx, "DelPut", err, "PutID", put.PutID)
|
||||
}
|
||||
return &third.ConfirmPutResp{
|
||||
Url: c.urlName(o.Name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *s3Database) GetUrl(ctx context.Context, req *third.GetUrlReq) (*third.GetUrlResp, error) {
|
||||
info, err := c.info.Take(ctx, req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.ValidTime != nil && info.ValidTime.Before(time.Now()) {
|
||||
return nil, errs.ErrRecordNotFound.Wrap("object expired")
|
||||
}
|
||||
hash, err := c.hash.Take(ctx, info.Hash, c.obj.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opt := obj.HeaderOption{ContentType: info.ContentType}
|
||||
if req.Attachment {
|
||||
opt.Filename = info.Name
|
||||
}
|
||||
u, err := c.obj.PresignedGetURL(ctx, hash.Bucket, hash.Name, time.Duration(req.Expires)*time.Millisecond, &opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &third.GetUrlResp{
|
||||
Url: u,
|
||||
Size: hash.Size,
|
||||
Hash: hash.Hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *s3Database) CleanExpirationObject(ctx context.Context, t time.Time) {
|
||||
// 清理上传产生的临时文件
|
||||
c.cleanPutTemp(ctx, t, 10)
|
||||
// 清理hash引用全过期的文件
|
||||
c.cleanExpirationObject(ctx, t)
|
||||
// 清理没有引用的hash对象
|
||||
c.clearNoCitation(ctx, c.obj.Name(), 10)
|
||||
}
|
||||
|
||||
func (c *s3Database) cleanPutTemp(ctx context.Context, t time.Time, num int) {
|
||||
for {
|
||||
puts, err := c.put.FindExpirationPut(ctx, t, num)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "FindExpirationPut", err, "Time", t, "Num", num)
|
||||
return
|
||||
}
|
||||
if len(puts) == 0 {
|
||||
return
|
||||
}
|
||||
for _, put := range puts {
|
||||
err := c.obj.DeleteObject(ctx, &obj.BucketObject{Bucket: c.obj.TempBucket(), Name: put.Path})
|
||||
if err != nil {
|
||||
log.ZError(ctx, "DeleteObject", err, "Bucket", c.obj.TempBucket(), "Path", put.Path)
|
||||
return
|
||||
}
|
||||
}
|
||||
ids := utils.Slice(puts, func(e *relation.ObjectPutModel) string { return e.PutID })
|
||||
err = c.put.DelPut(ctx, ids)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "DelPut", err, "PutID", ids)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *s3Database) cleanExpirationObject(ctx context.Context, t time.Time) {
|
||||
err := c.info.DeleteExpiration(ctx, t)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "DeleteExpiration", err, "Time", t)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *s3Database) clearNoCitation(ctx context.Context, engine string, limit int) {
|
||||
for {
|
||||
list, err := c.hash.DeleteNoCitation(ctx, engine, limit)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "DeleteNoCitation", err, "Engine", engine, "Limit", limit)
|
||||
return
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return
|
||||
}
|
||||
var hasErr bool
|
||||
for _, h := range list {
|
||||
err := c.obj.DeleteObject(ctx, &obj.BucketObject{Bucket: h.Bucket, Name: h.Name})
|
||||
if err != nil {
|
||||
hasErr = true
|
||||
log.ZError(ctx, "DeleteObject", err, "Bucket", h.Bucket, "Path", h.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if hasErr {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *s3Database) GetHashInfo(ctx context.Context, req *third.GetHashInfoReq) (*third.GetHashInfoResp, error) {
|
||||
if err := c.CheckHash(req.Hash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o, err := c.hash.Take(ctx, req.Hash, c.obj.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &third.GetHashInfoResp{
|
||||
Hash: o.Hash,
|
||||
Size: o.Size,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
@@ -19,7 +33,13 @@ func NewThirdDatabase(cache cache.MsgModel) ThirdDatabase {
|
||||
return &thirdDatabase{cache: cache}
|
||||
}
|
||||
|
||||
func (t *thirdDatabase) FcmUpdateToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) error {
|
||||
func (t *thirdDatabase) FcmUpdateToken(
|
||||
ctx context.Context,
|
||||
account string,
|
||||
platformID int,
|
||||
fcmToken string,
|
||||
expireTime int64,
|
||||
) error {
|
||||
return t.cache.SetFcmToken(ctx, account, platformID, fcmToken, expireTime)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 controller
|
||||
|
||||
import (
|
||||
@@ -31,7 +45,7 @@ type UserDatabase interface {
|
||||
//函数内部先查询db中是否存在,存在则什么都不做;不存在则插入
|
||||
InitOnce(ctx context.Context, users []*relation.UserModel) (err error)
|
||||
// 获取用户总数
|
||||
CountTotal(ctx context.Context) (int64, error)
|
||||
CountTotal(ctx context.Context, before *time.Time) (int64, error)
|
||||
// 获取范围内用户增量
|
||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||
}
|
||||
@@ -114,7 +128,10 @@ func (u *userDatabase) UpdateByMap(ctx context.Context, userID string, args map[
|
||||
}
|
||||
|
||||
// 获取,如果没找到,不返回错误
|
||||
func (u *userDatabase) Page(ctx context.Context, pageNumber, showNumber int32) (users []*relation.UserModel, count int64, err error) {
|
||||
func (u *userDatabase) Page(
|
||||
ctx context.Context,
|
||||
pageNumber, showNumber int32,
|
||||
) (users []*relation.UserModel, count int64, err error) {
|
||||
return u.userDB.Page(ctx, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
@@ -134,10 +151,14 @@ func (u *userDatabase) GetAllUserID(ctx context.Context) (userIDs []string, err
|
||||
return u.userDB.GetAllUserID(ctx)
|
||||
}
|
||||
|
||||
func (u *userDatabase) CountTotal(ctx context.Context) (count int64, err error) {
|
||||
return u.userDB.CountTotal(ctx)
|
||||
func (u *userDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
||||
return u.userDB.CountTotal(ctx, before)
|
||||
}
|
||||
|
||||
func (u *userDatabase) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
|
||||
func (u *userDatabase) CountRangeEverydayTotal(
|
||||
ctx context.Context,
|
||||
start time.Time,
|
||||
end time.Time,
|
||||
) (map[string]int64, error) {
|
||||
return u.userDB.CountRangeEverydayTotal(ctx, start, end)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 localcache
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 localcache
|
||||
|
||||
import (
|
||||
|
||||
@@ -1 +1,15 @@
|
||||
// Copyright © 2023 OpenIM. 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 localcache
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
package obj
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewMinioInterface() (Interface, error) {
|
||||
conf := config.Config.Object.Minio
|
||||
u, err := url.Parse(conf.Endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("minio endpoint parse %w", err)
|
||||
}
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return nil, fmt.Errorf("invalid minio endpoint scheme %s", u.Scheme)
|
||||
}
|
||||
client, err := minio.New(u.Host, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, ""),
|
||||
Secure: u.Scheme == "https",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("minio new client %w", err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
initBucket := func(ctx context.Context) error {
|
||||
for _, bucket := range utils.Distinct([]string{conf.TempBucket, conf.DataBucket}) {
|
||||
exists, err := client.BucketExists(ctx, bucket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("minio bucket %s exists %w", bucket, err)
|
||||
}
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
opt := minio.MakeBucketOptions{
|
||||
Region: conf.Location,
|
||||
ObjectLocking: conf.IsDistributedMod,
|
||||
}
|
||||
if err := client.MakeBucket(ctx, bucket, opt); err != nil {
|
||||
return fmt.Errorf("minio make bucket %s %w", bucket, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := initBucket(ctx); err != nil {
|
||||
fmt.Println("minio init error:", err)
|
||||
}
|
||||
return &minioImpl{
|
||||
client: client,
|
||||
tempBucket: conf.TempBucket,
|
||||
dataBucket: conf.DataBucket,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type minioImpl struct {
|
||||
tempBucket string // 上传桶
|
||||
dataBucket string // 永久桶
|
||||
urlstr string // 访问地址
|
||||
client *minio.Client
|
||||
}
|
||||
|
||||
func (m *minioImpl) Name() string {
|
||||
return "minio"
|
||||
}
|
||||
|
||||
func (m *minioImpl) MinFragmentSize() int64 {
|
||||
return 1024 * 1024 * 5 // 每个分片最小大小 minio.absMinPartSize
|
||||
}
|
||||
|
||||
func (m *minioImpl) MaxFragmentNum() int {
|
||||
return 1000 // 最大分片数量 minio.maxPartsCount
|
||||
}
|
||||
|
||||
func (m *minioImpl) MinExpirationTime() time.Duration {
|
||||
return time.Hour * 24
|
||||
}
|
||||
|
||||
func (m *minioImpl) TempBucket() string {
|
||||
return m.tempBucket
|
||||
}
|
||||
|
||||
func (m *minioImpl) DataBucket() string {
|
||||
return m.dataBucket
|
||||
}
|
||||
|
||||
func (m *minioImpl) PresignedGetURL(ctx context.Context, bucket string, name string, expires time.Duration, opt *HeaderOption) (string, error) {
|
||||
var reqParams url.Values
|
||||
if opt != nil {
|
||||
reqParams = make(url.Values)
|
||||
if opt.ContentType != "" {
|
||||
reqParams.Set("response-content-type", opt.ContentType)
|
||||
}
|
||||
if opt.Filename != "" {
|
||||
reqParams.Set("response-content-disposition", "attachment;filename="+opt.Filename)
|
||||
}
|
||||
}
|
||||
u, err := m.client.PresignedGetObject(ctx, bucket, name, expires, reqParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (m *minioImpl) PresignedPutURL(ctx context.Context, args *ApplyPutArgs) (string, error) {
|
||||
if args.Effective <= 0 {
|
||||
return "", errors.New("EffectiveTime <= 0")
|
||||
}
|
||||
_, err := m.GetObjectInfo(ctx, &BucketObject{
|
||||
Bucket: m.tempBucket,
|
||||
Name: args.Name,
|
||||
})
|
||||
if err == nil {
|
||||
return "", fmt.Errorf("minio bucket %s name %s already exists", args.Bucket, args.Name)
|
||||
} else if !m.IsNotFound(err) {
|
||||
return "", err
|
||||
}
|
||||
u, err := m.client.PresignedPutObject(ctx, m.tempBucket, args.Name, args.Effective)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("minio apply error: %w", err)
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (m *minioImpl) GetObjectInfo(ctx context.Context, args *BucketObject) (*ObjectInfo, error) {
|
||||
info, err := m.client.StatObject(ctx, args.Bucket, args.Name, minio.StatObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ObjectInfo{
|
||||
Size: info.Size,
|
||||
Hash: info.ETag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *minioImpl) CopyObject(ctx context.Context, src *BucketObject, dst *BucketObject) error {
|
||||
_, err := m.client.CopyObject(ctx, minio.CopyDestOptions{
|
||||
Bucket: dst.Bucket,
|
||||
Object: dst.Name,
|
||||
}, minio.CopySrcOptions{
|
||||
Bucket: src.Bucket,
|
||||
Object: src.Name,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *minioImpl) DeleteObject(ctx context.Context, info *BucketObject) error {
|
||||
return m.client.RemoveObject(ctx, info.Bucket, info.Name, minio.RemoveObjectOptions{})
|
||||
}
|
||||
|
||||
func (m *minioImpl) MoveObjectInfo(ctx context.Context, src *BucketObject, dst *BucketObject) error {
|
||||
if err := m.CopyObject(ctx, src, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.DeleteObject(ctx, src)
|
||||
}
|
||||
|
||||
func (m *minioImpl) ComposeObject(ctx context.Context, src []BucketObject, dst *BucketObject) error {
|
||||
destOptions := minio.CopyDestOptions{
|
||||
Bucket: dst.Bucket,
|
||||
Object: dst.Name + ".temp",
|
||||
}
|
||||
sources := make([]minio.CopySrcOptions, len(src))
|
||||
for i, s := range src {
|
||||
sources[i] = minio.CopySrcOptions{
|
||||
Bucket: s.Bucket,
|
||||
Object: s.Name,
|
||||
}
|
||||
}
|
||||
_, err := m.client.ComposeObject(ctx, destOptions, sources...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.MoveObjectInfo(ctx, &BucketObject{
|
||||
Bucket: destOptions.Bucket,
|
||||
Name: destOptions.Object,
|
||||
}, &BucketObject{
|
||||
Bucket: dst.Bucket,
|
||||
Name: dst.Name,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *minioImpl) IsNotFound(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
switch e := err.(type) {
|
||||
case minio.ErrorResponse:
|
||||
return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
|
||||
case *minio.ErrorResponse:
|
||||
return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (m *minioImpl) PutObject(ctx context.Context, info *BucketObject, reader io.Reader, size int64) (*ObjectInfo, error) {
|
||||
update, err := m.client.PutObject(ctx, info.Bucket, info.Name, reader, size, minio.PutObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ObjectInfo{
|
||||
Size: update.Size,
|
||||
Hash: update.ETag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *minioImpl) GetObject(ctx context.Context, info *BucketObject) (SizeReader, error) {
|
||||
object, err := m.client.GetObject(ctx, info.Bucket, info.Name, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stat, err := object.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewSizeReader(object, stat.Size), nil
|
||||
}
|
||||
|
||||
func (m *minioImpl) CheckName(name string) error {
|
||||
return s3utils.CheckValidObjectName(name)
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package obj
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BucketObject struct {
|
||||
Bucket string `json:"bucket"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ApplyPutArgs struct {
|
||||
Bucket string
|
||||
Name string
|
||||
Effective time.Duration // 申请有效时间
|
||||
Header http.Header // header
|
||||
MaxObjectSize int64
|
||||
}
|
||||
|
||||
type HeaderOption struct {
|
||||
ContentType string
|
||||
Filename string
|
||||
}
|
||||
|
||||
type ObjectInfo struct {
|
||||
Size int64
|
||||
Hash string
|
||||
}
|
||||
|
||||
type SizeReader interface {
|
||||
io.ReadCloser
|
||||
Size() int64
|
||||
}
|
||||
|
||||
func NewSizeReader(r io.ReadCloser, size int64) SizeReader {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return &sizeReader{
|
||||
size: size,
|
||||
ReadCloser: r,
|
||||
}
|
||||
}
|
||||
|
||||
type sizeReader struct {
|
||||
size int64
|
||||
io.ReadCloser
|
||||
}
|
||||
|
||||
func (r *sizeReader) Size() int64 {
|
||||
return r.size
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
// Name 存储名字
|
||||
Name() string
|
||||
// MinFragmentSize 最小允许的分片大小
|
||||
MinFragmentSize() int64
|
||||
// MaxFragmentNum 最大允许的分片数量
|
||||
MaxFragmentNum() int
|
||||
// MinExpirationTime 最小过期时间
|
||||
MinExpirationTime() time.Duration
|
||||
// TempBucket 临时桶名,用于上传
|
||||
TempBucket() string
|
||||
// DataBucket 永久存储的桶名
|
||||
DataBucket() string
|
||||
// PresignedGetURL 通过桶名和对象名返回URL
|
||||
PresignedGetURL(ctx context.Context, bucket string, name string, expires time.Duration, opt *HeaderOption) (string, error)
|
||||
// PresignedPutURL 申请上传,返回PUT的上传地址
|
||||
PresignedPutURL(ctx context.Context, args *ApplyPutArgs) (string, error)
|
||||
// GetObjectInfo 获取对象信息
|
||||
GetObjectInfo(ctx context.Context, args *BucketObject) (*ObjectInfo, error)
|
||||
// CopyObject 复制对象
|
||||
CopyObject(ctx context.Context, src *BucketObject, dst *BucketObject) error
|
||||
// DeleteObject 删除对象(不存在返回nil)
|
||||
DeleteObject(ctx context.Context, info *BucketObject) error
|
||||
// ComposeObject 合并对象
|
||||
ComposeObject(ctx context.Context, src []BucketObject, dst *BucketObject) error
|
||||
// IsNotFound 判断是不是不存在导致的错误
|
||||
IsNotFound(err error) bool
|
||||
// CheckName 检查名字是否可用
|
||||
CheckName(name string) error
|
||||
// PutObject 上传文件
|
||||
PutObject(ctx context.Context, info *BucketObject, reader io.Reader, size int64) (*ObjectInfo, error)
|
||||
// GetObject 下载文件
|
||||
GetObject(ctx context.Context, info *BucketObject) (SizeReader, error)
|
||||
}
|
||||
@@ -1,10 +1,26 @@
|
||||
// Copyright © 2023 OpenIM. 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 ormutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"gorm.io/gorm"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
)
|
||||
|
||||
func GormPage[E any](db *gorm.DB, pageNumber, showNumber int32) (uint32, []*E, error) {
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -5,9 +19,10 @@ import (
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type BlackGorm struct {
|
||||
@@ -26,37 +41,64 @@ func (b *BlackGorm) Delete(ctx context.Context, blacks []*relation.BlackModel) (
|
||||
return utils.Wrap(b.db(ctx).Delete(blacks).Error, "")
|
||||
}
|
||||
|
||||
func (b *BlackGorm) UpdateByMap(ctx context.Context, ownerUserID, blockUserID string, args map[string]interface{}) (err error) {
|
||||
return utils.Wrap(b.db(ctx).Where("block_user_id = ? and block_user_id = ?", ownerUserID, blockUserID).Updates(args).Error, "")
|
||||
func (b *BlackGorm) UpdateByMap(
|
||||
ctx context.Context,
|
||||
ownerUserID, blockUserID string,
|
||||
args map[string]interface{},
|
||||
) (err error) {
|
||||
return utils.Wrap(
|
||||
b.db(ctx).Where("block_user_id = ? and block_user_id = ?", ownerUserID, blockUserID).Updates(args).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
func (b *BlackGorm) Update(ctx context.Context, blacks []*relation.BlackModel) (err error) {
|
||||
return utils.Wrap(b.db(ctx).Updates(&blacks).Error, "")
|
||||
}
|
||||
|
||||
func (b *BlackGorm) Find(ctx context.Context, blacks []*relation.BlackModel) (blackList []*relation.BlackModel, err error) {
|
||||
func (b *BlackGorm) Find(
|
||||
ctx context.Context,
|
||||
blacks []*relation.BlackModel,
|
||||
) (blackList []*relation.BlackModel, err error) {
|
||||
var where [][]interface{}
|
||||
for _, black := range blacks {
|
||||
where = append(where, []interface{}{black.OwnerUserID, black.BlockUserID})
|
||||
}
|
||||
return blackList, utils.Wrap(b.db(ctx).Where("(owner_user_id, block_user_id) in ?", where).Find(&blackList).Error, "")
|
||||
return blackList, utils.Wrap(
|
||||
b.db(ctx).Where("(owner_user_id, block_user_id) in ?", where).Find(&blackList).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
func (b *BlackGorm) Take(ctx context.Context, ownerUserID, blockUserID string) (black *relation.BlackModel, err error) {
|
||||
black = &relation.BlackModel{}
|
||||
return black, utils.Wrap(b.db(ctx).Where("owner_user_id = ? and block_user_id = ?", ownerUserID, blockUserID).Take(black).Error, "")
|
||||
return black, utils.Wrap(
|
||||
b.db(ctx).Where("owner_user_id = ? and block_user_id = ?", ownerUserID, blockUserID).Take(black).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
func (b *BlackGorm) FindOwnerBlacks(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (blacks []*relation.BlackModel, total int64, err error) {
|
||||
func (b *BlackGorm) FindOwnerBlacks(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (blacks []*relation.BlackModel, total int64, err error) {
|
||||
err = b.db(ctx).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, utils.Wrap(err, "")
|
||||
}
|
||||
totalUint32, blacks, err := ormutil.GormPage[relation.BlackModel](b.db(ctx).Where("owner_user_id = ?", ownerUserID), pageNumber, showNumber)
|
||||
totalUint32, blacks, err := ormutil.GormPage[relation.BlackModel](
|
||||
b.db(ctx).Where("owner_user_id = ?", ownerUserID),
|
||||
pageNumber,
|
||||
showNumber,
|
||||
)
|
||||
total = int64(totalUint32)
|
||||
return
|
||||
}
|
||||
|
||||
func (b *BlackGorm) FindBlackUserIDs(ctx context.Context, ownerUserID string) (blackUserIDs []string, err error) {
|
||||
return blackUserIDs, utils.Wrap(b.db(ctx).Where("owner_user_id = ?", ownerUserID).Pluck("block_user_id", &blackUserIDs).Error, "")
|
||||
return blackUserIDs, utils.Wrap(
|
||||
b.db(ctx).Where("owner_user_id = ?", ownerUserID).Pluck("block_user_id", &blackUserIDs).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/jinzhu/copier"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
pbMsg "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg"
|
||||
sdkws "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/jinzhu/copier"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ChatLogGorm struct {
|
||||
@@ -48,7 +63,11 @@ func (c *ChatLogGorm) Create(msg *pbMsg.MsgDataToMQ) error {
|
||||
return c.DB.Create(chatLog).Error
|
||||
}
|
||||
|
||||
func (c *ChatLogGorm) GetChatLog(chatLog *relation.ChatLogModel, pageNumber, showNumber int32, contentTypeList []int32) (int64, []relation.ChatLogModel, error) {
|
||||
func (c *ChatLogGorm) GetChatLog(
|
||||
chatLog *relation.ChatLogModel,
|
||||
pageNumber, showNumber int32,
|
||||
contentTypeList []int32,
|
||||
) (int64, []relation.ChatLogModel, error) {
|
||||
mdb := c.DB.Model(chatLog)
|
||||
if chatLog.SendTime.Unix() > 0 {
|
||||
mdb = mdb.Where("send_time > ? and send_time < ?", chatLog.SendTime, chatLog.SendTime.AddDate(0, 0, 1))
|
||||
|
||||
@@ -82,15 +82,13 @@ func (c *ConversationGorm) GetAllConversationIDs(ctx context.Context) (conversat
|
||||
}
|
||||
|
||||
func (c *ConversationGorm) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (hasReadSeqs map[string]int64, err error) {
|
||||
var conversations []*relation.ConversationModel
|
||||
err = utils.Wrap(c.db(ctx).Where("owner_user_id = ?", ownerUserID).Select("conversation_id", "has_read_seq").Find(&conversations).Error, "")
|
||||
hasReadSeqs = make(map[string]int64, len(conversations))
|
||||
// for _, conversation := range conversations {
|
||||
// hasReadSeqs[conversation.ConversationID] = conversation.HasReadSeq
|
||||
// }
|
||||
return hasReadSeqs, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *ConversationGorm) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) (conversations []*relation.ConversationModel, err error) {
|
||||
return conversations, utils.Wrap(c.db(ctx).Where("conversation_id IN (?)", conversationIDs).Find(&conversations).Error, "")
|
||||
}
|
||||
|
||||
func (c *ConversationGorm) GetConversationIDsNeedDestruct(ctx context.Context) (conversations []*relation.ConversationModel, err error) {
|
||||
return conversations, utils.Wrap(c.db(ctx).Where("is_msg_destruct = 1 && UNIX_TIMESTAMP(NOW()) > (msg_destruct_time + UNIX_TIMESTAMP(latest_msg_destruct_time)) && msg_destruct_time != 0").Find(&conversations).Error, "")
|
||||
}
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type FriendGorm struct {
|
||||
@@ -27,13 +42,27 @@ func (f *FriendGorm) Create(ctx context.Context, friends []*relation.FriendModel
|
||||
|
||||
// 删除ownerUserID指定的好友
|
||||
func (f *FriendGorm) Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) (err error) {
|
||||
err = utils.Wrap(f.db(ctx).Where("owner_user_id = ? AND friend_user_id in ( ?)", ownerUserID, friendUserIDs).Delete(&relation.FriendModel{}).Error, "")
|
||||
err = utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("owner_user_id = ? AND friend_user_id in ( ?)", ownerUserID, friendUserIDs).
|
||||
Delete(&relation.FriendModel{}).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新ownerUserID单个好友信息 更新零值
|
||||
func (f *FriendGorm) UpdateByMap(ctx context.Context, ownerUserID string, friendUserID string, args map[string]interface{}) (err error) {
|
||||
return utils.Wrap(f.db(ctx).Where("owner_user_id = ? AND friend_user_id = ? ", ownerUserID, friendUserID).Updates(args).Error, "")
|
||||
func (f *FriendGorm) UpdateByMap(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
friendUserID string,
|
||||
args map[string]interface{},
|
||||
) (err error) {
|
||||
return utils.Wrap(
|
||||
f.db(ctx).Where("owner_user_id = ? AND friend_user_id = ? ", ownerUserID, friendUserID).Updates(args).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// 更新好友信息的非零值
|
||||
@@ -44,7 +73,13 @@ func (f *FriendGorm) Update(ctx context.Context, friends []*relation.FriendModel
|
||||
// 更新好友备注(也支持零值 )
|
||||
func (f *FriendGorm) UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error) {
|
||||
if remark != "" {
|
||||
return utils.Wrap(f.db(ctx).Where("owner_user_id = ? and friend_user_id = ?", ownerUserID, friendUserID).Update("remark", remark).Error, "")
|
||||
return utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("owner_user_id = ? and friend_user_id = ?", ownerUserID, friendUserID).
|
||||
Update("remark", remark).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
m := make(map[string]interface{}, 1)
|
||||
m["remark"] = ""
|
||||
@@ -52,46 +87,106 @@ func (f *FriendGorm) UpdateRemark(ctx context.Context, ownerUserID, friendUserID
|
||||
}
|
||||
|
||||
// 获取单个好友信息,如没找到 返回错误
|
||||
func (f *FriendGorm) Take(ctx context.Context, ownerUserID, friendUserID string) (friend *relation.FriendModel, err error) {
|
||||
func (f *FriendGorm) Take(
|
||||
ctx context.Context,
|
||||
ownerUserID, friendUserID string,
|
||||
) (friend *relation.FriendModel, err error) {
|
||||
friend = &relation.FriendModel{}
|
||||
return friend, utils.Wrap(f.db(ctx).Where("owner_user_id = ? and friend_user_id", ownerUserID, friendUserID).Take(friend).Error, "")
|
||||
return friend, utils.Wrap(
|
||||
f.db(ctx).Where("owner_user_id = ? and friend_user_id", ownerUserID, friendUserID).Take(friend).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// 查找好友关系,如果是双向关系,则都返回
|
||||
func (f *FriendGorm) FindUserState(ctx context.Context, userID1, userID2 string) (friends []*relation.FriendModel, err error) {
|
||||
return friends, utils.Wrap(f.db(ctx).Where("(owner_user_id = ? and friend_user_id = ?) or (owner_user_id = ? and friend_user_id = ?)", userID1, userID2, userID2, userID1).Find(&friends).Error, "")
|
||||
func (f *FriendGorm) FindUserState(
|
||||
ctx context.Context,
|
||||
userID1, userID2 string,
|
||||
) (friends []*relation.FriendModel, err error) {
|
||||
return friends, utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("(owner_user_id = ? and friend_user_id = ?) or (owner_user_id = ? and friend_user_id = ?)", userID1, userID2, userID2, userID1).
|
||||
Find(&friends).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// 获取 owner指定的好友列表 如果有friendUserIDs不存在,也不返回错误
|
||||
func (f *FriendGorm) FindFriends(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*relation.FriendModel, err error) {
|
||||
return friends, utils.Wrap(f.db(ctx).Where("owner_user_id = ? AND friend_user_id in (?)", ownerUserID, friendUserIDs).Find(&friends).Error, "")
|
||||
func (f *FriendGorm) FindFriends(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
friendUserIDs []string,
|
||||
) (friends []*relation.FriendModel, err error) {
|
||||
return friends, utils.Wrap(
|
||||
f.db(ctx).Where("owner_user_id = ? AND friend_user_id in (?)", ownerUserID, friendUserIDs).Find(&friends).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// 获取哪些人添加了friendUserID 如果有ownerUserIDs不存在,也不返回错误
|
||||
func (f *FriendGorm) FindReversalFriends(ctx context.Context, friendUserID string, ownerUserIDs []string) (friends []*relation.FriendModel, err error) {
|
||||
return friends, utils.Wrap(f.db(ctx).Where("friend_user_id = ? AND owner_user_id in (?)", friendUserID, ownerUserIDs).Find(&friends).Error, "")
|
||||
func (f *FriendGorm) FindReversalFriends(
|
||||
ctx context.Context,
|
||||
friendUserID string,
|
||||
ownerUserIDs []string,
|
||||
) (friends []*relation.FriendModel, err error) {
|
||||
return friends, utils.Wrap(
|
||||
f.db(ctx).Where("friend_user_id = ? AND owner_user_id in (?)", friendUserID, ownerUserIDs).Find(&friends).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// 获取ownerUserID好友列表 支持翻页
|
||||
func (f *FriendGorm) FindOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) {
|
||||
func (f *FriendGorm) FindOwnerFriends(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendModel, total int64, err error) {
|
||||
err = f.DB.Model(&relation.FriendModel{}).Where("owner_user_id = ? ", ownerUserID).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, utils.Wrap(err, "")
|
||||
}
|
||||
err = utils.Wrap(f.db(ctx).Where("owner_user_id = ? ", ownerUserID).Limit(int(showNumber)).Offset(int((pageNumber-1)*showNumber)).Find(&friends).Error, "")
|
||||
err = utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("owner_user_id = ? ", ownerUserID).
|
||||
Limit(int(showNumber)).
|
||||
Offset(int((pageNumber-1)*showNumber)).
|
||||
Find(&friends).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取哪些人添加了friendUserID 支持翻页
|
||||
func (f *FriendGorm) FindInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) (friends []*relation.FriendModel, total int64, err error) {
|
||||
func (f *FriendGorm) FindInWhoseFriends(
|
||||
ctx context.Context,
|
||||
friendUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*relation.FriendModel, total int64, err error) {
|
||||
err = f.DB.Model(&relation.FriendModel{}).Where("friend_user_id = ? ", friendUserID).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, utils.Wrap(err, "")
|
||||
}
|
||||
err = utils.Wrap(f.db(ctx).Where("friend_user_id = ? ", friendUserID).Limit(int(showNumber)).Offset(int((pageNumber-1)*showNumber)).Find(&friends).Error, "")
|
||||
err = utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("friend_user_id = ? ", friendUserID).
|
||||
Limit(int(showNumber)).
|
||||
Offset(int((pageNumber-1)*showNumber)).
|
||||
Find(&friends).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FriendGorm) FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) {
|
||||
return friendUserIDs, utils.Wrap(f.db(ctx).Model(&relation.FriendModel{}).Where("owner_user_id = ? ", ownerUserID).Pluck("friend_user_id", &friendUserIDs).Error, "")
|
||||
return friendUserIDs, utils.Wrap(
|
||||
f.db(ctx).
|
||||
Model(&relation.FriendModel{}).
|
||||
Where("owner_user_id = ? ", ownerUserID).
|
||||
Pluck("friend_user_id", &friendUserIDs).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type FriendRequestGorm struct {
|
||||
@@ -27,48 +42,108 @@ func (f *FriendRequestGorm) Create(ctx context.Context, friendRequests []*relati
|
||||
|
||||
// 删除记录
|
||||
func (f *FriendRequestGorm) Delete(ctx context.Context, fromUserID, toUserID string) (err error) {
|
||||
return utils.Wrap(f.db(ctx).Where("from_user_id = ? AND to_user_id = ?", fromUserID, toUserID).Delete(&relation.FriendRequestModel{}).Error, "")
|
||||
return utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("from_user_id = ? AND to_user_id = ?", fromUserID, toUserID).
|
||||
Delete(&relation.FriendRequestModel{}).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// 更新零值
|
||||
func (f *FriendRequestGorm) UpdateByMap(ctx context.Context, fromUserID string, toUserID string, args map[string]interface{}) (err error) {
|
||||
return utils.Wrap(f.db(ctx).Model(&relation.FriendRequestModel{}).Where("from_user_id = ? AND to_user_id =?", fromUserID, toUserID).Updates(args).Error, "")
|
||||
func (f *FriendRequestGorm) UpdateByMap(
|
||||
ctx context.Context,
|
||||
fromUserID string,
|
||||
toUserID string,
|
||||
args map[string]interface{},
|
||||
) (err error) {
|
||||
return utils.Wrap(
|
||||
f.db(ctx).
|
||||
Model(&relation.FriendRequestModel{}).
|
||||
Where("from_user_id = ? AND to_user_id =?", fromUserID, toUserID).
|
||||
Updates(args).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// 更新记录 (非零值)
|
||||
func (f *FriendRequestGorm) Update(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) {
|
||||
return utils.Wrap(f.db(ctx).Where("from_user_id = ? AND to_user_id =?", friendRequest.FromUserID, friendRequest.ToUserID).Updates(friendRequest).Error, "")
|
||||
return utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("from_user_id = ? AND to_user_id =?", friendRequest.FromUserID, friendRequest.ToUserID).
|
||||
Updates(friendRequest).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// 获取来指定用户的好友申请 未找到 不返回错误
|
||||
func (f *FriendRequestGorm) Find(ctx context.Context, fromUserID, toUserID string) (friendRequest *relation.FriendRequestModel, err error) {
|
||||
func (f *FriendRequestGorm) Find(
|
||||
ctx context.Context,
|
||||
fromUserID, toUserID string,
|
||||
) (friendRequest *relation.FriendRequestModel, err error) {
|
||||
friendRequest = &relation.FriendRequestModel{}
|
||||
err = utils.Wrap(f.db(ctx).Where("from_user_id = ? and to_user_id = ?", fromUserID, toUserID).Find(friendRequest).Error, "")
|
||||
err = utils.Wrap(
|
||||
f.db(ctx).Where("from_user_id = ? and to_user_id = ?", fromUserID, toUserID).Find(friendRequest).Error,
|
||||
"",
|
||||
)
|
||||
return friendRequest, err
|
||||
}
|
||||
|
||||
func (f *FriendRequestGorm) Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *relation.FriendRequestModel, err error) {
|
||||
func (f *FriendRequestGorm) Take(
|
||||
ctx context.Context,
|
||||
fromUserID, toUserID string,
|
||||
) (friendRequest *relation.FriendRequestModel, err error) {
|
||||
friendRequest = &relation.FriendRequestModel{}
|
||||
err = utils.Wrap(f.db(ctx).Where("from_user_id = ? and to_user_id = ?", fromUserID, toUserID).Take(friendRequest).Error, "")
|
||||
err = utils.Wrap(
|
||||
f.db(ctx).Where("from_user_id = ? and to_user_id = ?", fromUserID, toUserID).Take(friendRequest).Error,
|
||||
"",
|
||||
)
|
||||
return friendRequest, err
|
||||
}
|
||||
|
||||
// 获取toUserID收到的好友申请列表
|
||||
func (f *FriendRequestGorm) FindToUserID(ctx context.Context, toUserID string, pageNumber, showNumber int32) (friendRequests []*relation.FriendRequestModel, total int64, err error) {
|
||||
func (f *FriendRequestGorm) FindToUserID(
|
||||
ctx context.Context,
|
||||
toUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friendRequests []*relation.FriendRequestModel, total int64, err error) {
|
||||
err = f.db(ctx).Model(&relation.FriendRequestModel{}).Where("to_user_id = ? ", toUserID).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, utils.Wrap(err, "")
|
||||
}
|
||||
err = utils.Wrap(f.db(ctx).Where("to_user_id = ? ", toUserID).Limit(int(showNumber)).Offset(int(pageNumber-1)*int(showNumber)).Find(&friendRequests).Error, "")
|
||||
err = utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("to_user_id = ? ", toUserID).
|
||||
Limit(int(showNumber)).
|
||||
Offset(int(pageNumber-1)*int(showNumber)).
|
||||
Find(&friendRequests).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取fromUserID发出去的好友申请列表
|
||||
func (f *FriendRequestGorm) FindFromUserID(ctx context.Context, fromUserID string, pageNumber, showNumber int32) (friendRequests []*relation.FriendRequestModel, total int64, err error) {
|
||||
func (f *FriendRequestGorm) FindFromUserID(
|
||||
ctx context.Context,
|
||||
fromUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friendRequests []*relation.FriendRequestModel, total int64, err error) {
|
||||
err = f.db(ctx).Model(&relation.FriendRequestModel{}).Where("from_user_id = ? ", fromUserID).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, utils.Wrap(err, "")
|
||||
}
|
||||
err = utils.Wrap(f.db(ctx).Where("from_user_id = ? ", fromUserID).Limit(int(showNumber)).Offset(int(pageNumber-1)*int(showNumber)).Find(&friendRequests).Error, "")
|
||||
err = utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("from_user_id = ? ", fromUserID).
|
||||
Limit(int(showNumber)).
|
||||
Offset(int(pageNumber-1)*int(showNumber)).
|
||||
Find(&friendRequests).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var _ relation.GroupMemberModelInterface = (*GroupMemberGorm)(nil)
|
||||
@@ -29,7 +44,10 @@ func (g *GroupMemberGorm) Create(ctx context.Context, groupMemberList []*relatio
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) Delete(ctx context.Context, groupID string, userIDs []string) (err error) {
|
||||
return utils.Wrap(g.db(ctx).Where("group_id = ? and user_id in (?)", groupID, userIDs).Delete(&relation.GroupMemberModel{}).Error, "")
|
||||
return utils.Wrap(
|
||||
g.db(ctx).Where("group_id = ? and user_id in (?)", groupID, userIDs).Delete(&relation.GroupMemberModel{}).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) DeleteGroup(ctx context.Context, groupIDs []string) (err error) {
|
||||
@@ -40,14 +58,24 @@ func (g *GroupMemberGorm) Update(ctx context.Context, groupID string, userID str
|
||||
return utils.Wrap(g.db(ctx).Where("group_id = ? and user_id = ?", groupID, userID).Updates(data).Error, "")
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) (rowsAffected int64, err error) {
|
||||
func (g *GroupMemberGorm) UpdateRoleLevel(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
roleLevel int32,
|
||||
) (rowsAffected int64, err error) {
|
||||
db := g.db(ctx).Where("group_id = ? and user_id = ?", groupID, userID).Updates(map[string]any{
|
||||
"role_level": roleLevel,
|
||||
})
|
||||
return db.RowsAffected, utils.Wrap(db.Error, "")
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) Find(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) (groupMembers []*relation.GroupMemberModel, err error) {
|
||||
func (g *GroupMemberGorm) Find(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
) (groupMembers []*relation.GroupMemberModel, err error) {
|
||||
db := g.db(ctx)
|
||||
if len(groupIDs) > 0 {
|
||||
db = db.Where("group_id in (?)", groupIDs)
|
||||
@@ -61,17 +89,37 @@ func (g *GroupMemberGorm) Find(ctx context.Context, groupIDs []string, userIDs [
|
||||
return groupMembers, utils.Wrap(db.Find(&groupMembers).Error, "")
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) Take(ctx context.Context, groupID string, userID string) (groupMember *relation.GroupMemberModel, err error) {
|
||||
func (g *GroupMemberGorm) Take(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
) (groupMember *relation.GroupMemberModel, err error) {
|
||||
groupMember = &relation.GroupMemberModel{}
|
||||
return groupMember, utils.Wrap(g.db(ctx).Where("group_id = ? and user_id = ?", groupID, userID).Take(groupMember).Error, "")
|
||||
return groupMember, utils.Wrap(
|
||||
g.db(ctx).Where("group_id = ? and user_id = ?", groupID, userID).Take(groupMember).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) TakeOwner(ctx context.Context, groupID string) (groupMember *relation.GroupMemberModel, err error) {
|
||||
func (g *GroupMemberGorm) TakeOwner(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
) (groupMember *relation.GroupMemberModel, err error) {
|
||||
groupMember = &relation.GroupMemberModel{}
|
||||
return groupMember, utils.Wrap(g.db(ctx).Where("group_id = ? and role_level = ?", groupID, constant.GroupOwner).Take(groupMember).Error, "")
|
||||
return groupMember, utils.Wrap(
|
||||
g.db(ctx).Where("group_id = ? and role_level = ?", groupID, constant.GroupOwner).Take(groupMember).Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) SearchMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (total uint32, groupList []*relation.GroupMemberModel, err error) {
|
||||
func (g *GroupMemberGorm) SearchMember(
|
||||
ctx context.Context,
|
||||
keyword string,
|
||||
groupIDs []string,
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, groupList []*relation.GroupMemberModel, err error) {
|
||||
db := g.db(ctx)
|
||||
ormutil.GormIn(&db, "group_id", groupIDs)
|
||||
ormutil.GormIn(&db, "user_id", userIDs)
|
||||
@@ -79,11 +127,17 @@ func (g *GroupMemberGorm) SearchMember(ctx context.Context, keyword string, grou
|
||||
return ormutil.GormSearch[relation.GroupMemberModel](db, []string{"nickname"}, keyword, pageNumber, showNumber)
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) MapGroupMemberNum(ctx context.Context, groupIDs []string) (count map[string]uint32, err error) {
|
||||
func (g *GroupMemberGorm) MapGroupMemberNum(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
) (count map[string]uint32, err error) {
|
||||
return ormutil.MapCount(g.db(ctx).Where("group_id in (?)", groupIDs), "group_id")
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) FindJoinUserID(ctx context.Context, groupIDs []string) (groupUsers map[string][]string, err error) {
|
||||
func (g *GroupMemberGorm) FindJoinUserID(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
) (groupUsers map[string][]string, err error) {
|
||||
var groupMembers []*relation.GroupMemberModel
|
||||
if err := g.db(ctx).Select("group_id, user_id").Where("group_id in (?)", groupIDs).Find(&groupMembers).Error; err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
@@ -131,5 +185,12 @@ func (g *GroupMemberGorm) FindUsersJoinedGroupID(ctx context.Context, userIDs []
|
||||
}
|
||||
|
||||
func (g *GroupMemberGorm) FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) {
|
||||
return groupIDs, utils.Wrap(g.db(ctx).Model(&relation.GroupMemberModel{}).Where("user_id = ? and (role_level = ? or role_level = ?)", userID, constant.GroupOwner, constant.GroupAdmin).Pluck("group_id", &groupIDs).Error, "")
|
||||
return groupIDs, utils.Wrap(
|
||||
g.db(ctx).
|
||||
Model(&relation.GroupMemberModel{}).
|
||||
Where("user_id = ? and (role_level = ? or role_level = ?)", userID, constant.GroupOwner, constant.GroupAdmin).
|
||||
Pluck("group_id", &groupIDs).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ relation.GroupModelInterface = (*GroupGorm)(nil)
|
||||
@@ -50,3 +52,30 @@ func (g *GroupGorm) Search(ctx context.Context, keyword string, pageNumber, show
|
||||
func (g *GroupGorm) GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) {
|
||||
return groupIDs, utils.Wrap(g.DB.Model(&relation.GroupModel{}).Where("group_type = ? ", groupType).Pluck("group_id", &groupIDs).Error, "")
|
||||
}
|
||||
|
||||
func (g *GroupGorm) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
||||
db := g.db(ctx).Model(&relation.GroupModel{})
|
||||
if before != nil {
|
||||
db = db.Where("create_time < ?", before)
|
||||
}
|
||||
if err := db.Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (g *GroupGorm) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
|
||||
var res []struct {
|
||||
Date time.Time `gorm:"column:date"`
|
||||
Count int64 `gorm:"column:count"`
|
||||
}
|
||||
err := g.db(ctx).Model(&relation.GroupModel{}).Select("DATE(create_time) AS date, count(1) AS count").Where("create_time >= ? and create_time < ?", start, end).Group("date").Find(&res).Error
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
v := make(map[string]int64)
|
||||
for _, r := range res {
|
||||
v[r.Date.Format("2006-01-02")] = r.Count
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/ormutil"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type GroupRequestGorm struct {
|
||||
@@ -28,25 +44,69 @@ func (g *GroupRequestGorm) Create(ctx context.Context, groupRequests []*relation
|
||||
}
|
||||
|
||||
func (g *GroupRequestGorm) Delete(ctx context.Context, groupID string, userID string) (err error) {
|
||||
return utils.Wrap(g.DB.WithContext(ctx).Where("group_id = ? and user_id = ? ", groupID, userID).Delete(&relation.GroupRequestModel{}).Error, utils.GetSelfFuncName())
|
||||
return utils.Wrap(
|
||||
g.DB.WithContext(ctx).
|
||||
Where("group_id = ? and user_id = ? ", groupID, userID).
|
||||
Delete(&relation.GroupRequestModel{}).
|
||||
Error,
|
||||
utils.GetSelfFuncName(),
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupRequestGorm) UpdateHandler(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32) (err error) {
|
||||
return utils.Wrap(g.DB.WithContext(ctx).Model(&relation.GroupRequestModel{}).Where("group_id = ? and user_id = ? ", groupID, userID).Updates(map[string]any{
|
||||
"handle_msg": handledMsg,
|
||||
"handle_result": handleResult,
|
||||
}).Error, utils.GetSelfFuncName())
|
||||
func (g *GroupRequestGorm) UpdateHandler(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
handledMsg string,
|
||||
handleResult int32,
|
||||
) (err error) {
|
||||
return utils.Wrap(
|
||||
g.DB.WithContext(ctx).
|
||||
Model(&relation.GroupRequestModel{}).
|
||||
Where("group_id = ? and user_id = ? ", groupID, userID).
|
||||
Updates(map[string]any{
|
||||
"handle_msg": handledMsg,
|
||||
"handle_result": handleResult,
|
||||
}).
|
||||
Error,
|
||||
utils.GetSelfFuncName(),
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupRequestGorm) Take(ctx context.Context, groupID string, userID string) (groupRequest *relation.GroupRequestModel, err error) {
|
||||
func (g *GroupRequestGorm) Take(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
userID string,
|
||||
) (groupRequest *relation.GroupRequestModel, err error) {
|
||||
groupRequest = &relation.GroupRequestModel{}
|
||||
return groupRequest, utils.Wrap(g.DB.WithContext(ctx).Where("group_id = ? and user_id = ? ", groupID, userID).Take(groupRequest).Error, utils.GetSelfFuncName())
|
||||
return groupRequest, utils.Wrap(
|
||||
g.DB.WithContext(ctx).Where("group_id = ? and user_id = ? ", groupID, userID).Take(groupRequest).Error,
|
||||
utils.GetSelfFuncName(),
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupRequestGorm) Page(ctx context.Context, userID string, pageNumber, showNumber int32) (total uint32, groups []*relation.GroupRequestModel, err error) {
|
||||
return ormutil.GormSearch[relation.GroupRequestModel](g.DB.WithContext(ctx).Where("user_id = ?", userID), nil, "", pageNumber, showNumber)
|
||||
func (g *GroupRequestGorm) Page(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, groups []*relation.GroupRequestModel, err error) {
|
||||
return ormutil.GormSearch[relation.GroupRequestModel](
|
||||
g.DB.WithContext(ctx).Where("user_id = ?", userID),
|
||||
nil,
|
||||
"",
|
||||
pageNumber,
|
||||
showNumber,
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GroupRequestGorm) PageGroup(ctx context.Context, groupIDs []string, pageNumber, showNumber int32) (total uint32, groups []*relation.GroupRequestModel, err error) {
|
||||
return ormutil.GormPage[relation.GroupRequestModel](g.DB.WithContext(ctx).Where("group_id in ?", groupIDs), pageNumber, showNumber)
|
||||
func (g *GroupRequestGorm) PageGroup(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, groups []*relation.GroupRequestModel, err error) {
|
||||
return ormutil.GormPage[relation.GroupRequestModel](
|
||||
g.DB.WithContext(ctx).Where("group_id in ?", groupIDs),
|
||||
pageNumber,
|
||||
showNumber,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,43 +1,73 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
mysqlDriver "github.com/go-sql-driver/mysql"
|
||||
"gorm.io/driver/mysql"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
mysqlDriver "github.com/go-sql-driver/mysql"
|
||||
"gorm.io/driver/mysql"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
maxRetry = 100 //number of retries
|
||||
)
|
||||
|
||||
// newMysqlGormDB Initialize the database connection
|
||||
func newMysqlGormDB() (*gorm.DB, error) {
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local",
|
||||
config.Config.Mysql.Username, config.Config.Mysql.Password, config.Config.Mysql.Address[0], "mysql")
|
||||
db, err := gorm.Open(mysql.Open(dsn), nil)
|
||||
|
||||
db, err := connectToDatabase(dsn, maxRetry)
|
||||
if err != nil {
|
||||
time.Sleep(time.Duration(30) * time.Second)
|
||||
db, err = gorm.Open(mysql.Open(dsn), nil)
|
||||
if err != nil {
|
||||
panic(err.Error() + " open failed " + dsn)
|
||||
}
|
||||
panic(err.Error() + " Open failed " + dsn)
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer sqlDB.Close()
|
||||
sql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s default charset utf8mb4 COLLATE utf8mb4_unicode_ci;", config.Config.Mysql.Database)
|
||||
sql := fmt.Sprintf(
|
||||
"CREATE DATABASE IF NOT EXISTS %s default charset utf8mb4 COLLATE utf8mb4_unicode_ci;",
|
||||
config.Config.Mysql.Database,
|
||||
)
|
||||
err = db.Exec(sql).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("init db %w", err)
|
||||
}
|
||||
dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local",
|
||||
config.Config.Mysql.Username, config.Config.Mysql.Password, config.Config.Mysql.Address[0], config.Config.Mysql.Database)
|
||||
sqlLogger := log.NewSqlLogger(logger.LogLevel(config.Config.Mysql.LogLevel), true, time.Duration(config.Config.Mysql.SlowThreshold)*time.Millisecond)
|
||||
dsn = fmt.Sprintf(
|
||||
"%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local",
|
||||
config.Config.Mysql.Username,
|
||||
config.Config.Mysql.Password,
|
||||
config.Config.Mysql.Address[0],
|
||||
config.Config.Mysql.Database,
|
||||
)
|
||||
sqlLogger := log.NewSqlLogger(
|
||||
logger.LogLevel(config.Config.Mysql.LogLevel),
|
||||
true,
|
||||
time.Duration(config.Config.Mysql.SlowThreshold)*time.Millisecond,
|
||||
)
|
||||
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
Logger: sqlLogger,
|
||||
})
|
||||
@@ -54,7 +84,24 @@ func newMysqlGormDB() (*gorm.DB, error) {
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// gorm mysql
|
||||
// connectToDatabase Connection retry for mysql
|
||||
func connectToDatabase(dsn string, maxRetry int) (*gorm.DB, error) {
|
||||
var db *gorm.DB
|
||||
var err error
|
||||
for i := 0; i <= maxRetry; i++ {
|
||||
db, err = gorm.Open(mysql.Open(dsn), nil)
|
||||
if err == nil {
|
||||
return db, nil
|
||||
}
|
||||
if mysqlErr, ok := err.(*mysqlDriver.MySQLError); ok && mysqlErr.Number == 1045 {
|
||||
return nil, err
|
||||
}
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// NewGormDB gorm mysql
|
||||
func NewGormDB() (*gorm.DB, error) {
|
||||
specialerror.AddReplace(gorm.ErrRecordNotFound, errs.ErrRecordNotFound)
|
||||
specialerror.AddErrHandler(replaceDuplicateKey)
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ObjectHashGorm struct {
|
||||
*MetaDB
|
||||
}
|
||||
|
||||
func NewObjectHash(db *gorm.DB) relation.ObjectHashModelInterface {
|
||||
return &ObjectHashGorm{
|
||||
NewMetaDB(db, &relation.ObjectHashModel{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ObjectHashGorm) NewTx(tx any) relation.ObjectHashModelInterface {
|
||||
return &ObjectHashGorm{
|
||||
NewMetaDB(tx.(*gorm.DB), &relation.ObjectHashModel{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ObjectHashGorm) Take(ctx context.Context, hash string, engine string) (oh *relation.ObjectHashModel, err error) {
|
||||
oh = &relation.ObjectHashModel{}
|
||||
return oh, utils.Wrap1(o.DB.Where("hash = ? and engine = ?", hash, engine).Take(oh).Error)
|
||||
}
|
||||
|
||||
func (o *ObjectHashGorm) Create(ctx context.Context, h []*relation.ObjectHashModel) (err error) {
|
||||
return utils.Wrap1(o.DB.Create(h).Error)
|
||||
}
|
||||
|
||||
func (o *ObjectHashGorm) DeleteNoCitation(ctx context.Context, engine string, num int) (list []*relation.ObjectHashModel, err error) {
|
||||
err = o.DB.Table(relation.ObjectHashModelTableName, "as h").Select("h.*").
|
||||
Joins("LEFT JOIN "+relation.ObjectInfoModelTableName+" as i ON h.hash = i.hash").
|
||||
Where("h.engine = ? AND i.hash IS NULL", engine).
|
||||
Limit(num).
|
||||
Find(&list).Error
|
||||
return list, utils.Wrap1(err)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ObjectInfoGorm struct {
|
||||
*MetaDB
|
||||
}
|
||||
|
||||
func NewObjectInfo(db *gorm.DB) relation.ObjectInfoModelInterface {
|
||||
return &ObjectInfoGorm{
|
||||
NewMetaDB(db, &relation.ObjectInfoModel{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ObjectInfoGorm) NewTx(tx any) relation.ObjectInfoModelInterface {
|
||||
return &ObjectInfoGorm{
|
||||
NewMetaDB(tx.(*gorm.DB), &relation.ObjectInfoModel{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ObjectInfoGorm) SetObject(ctx context.Context, obj *relation.ObjectInfoModel) (err error) {
|
||||
if err := o.DB.WithContext(ctx).Where("name = ?", obj.Name).Delete(&relation.ObjectInfoModel{}).Error; err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return errs.Wrap(o.DB.WithContext(ctx).Create(obj).Error)
|
||||
//return errs.Wrap(o.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// if err := tx.Where("name = ?", obj.Name).Delete(&relation.ObjectInfoModel{}).Error; err != nil {
|
||||
// return errs.Wrap(err)
|
||||
// }
|
||||
// return errs.Wrap(tx.Create(obj).Error)
|
||||
//}))
|
||||
}
|
||||
|
||||
func (o *ObjectInfoGorm) Take(ctx context.Context, name string) (info *relation.ObjectInfoModel, err error) {
|
||||
info = &relation.ObjectInfoModel{}
|
||||
return info, utils.Wrap1(o.DB.WithContext(ctx).Where("name = ?", name).Take(info).Error)
|
||||
}
|
||||
|
||||
func (o *ObjectInfoGorm) DeleteExpiration(ctx context.Context, expiration time.Time) (err error) {
|
||||
return utils.Wrap1(o.DB.WithContext(ctx).Where("expiration_time IS NOT NULL AND expiration_time <= ?", expiration).Delete(&relation.ObjectInfoModel{}).Error)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ObjectInfoGorm struct {
|
||||
*MetaDB
|
||||
}
|
||||
|
||||
func NewObjectInfo(db *gorm.DB) relation.ObjectInfoModelInterface {
|
||||
return &ObjectInfoGorm{
|
||||
NewMetaDB(db, &relation.ObjectModel{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ObjectInfoGorm) NewTx(tx any) relation.ObjectInfoModelInterface {
|
||||
return &ObjectInfoGorm{
|
||||
NewMetaDB(tx.(*gorm.DB), &relation.ObjectModel{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ObjectInfoGorm) SetObject(ctx context.Context, obj *relation.ObjectModel) (err error) {
|
||||
if err := o.DB.WithContext(ctx).Where("name = ?", obj.Name).FirstOrCreate(obj).Error; err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ObjectInfoGorm) Take(ctx context.Context, name string) (info *relation.ObjectModel, err error) {
|
||||
info = &relation.ObjectModel{}
|
||||
return info, errs.Wrap(o.DB.WithContext(ctx).Where("name = ?", name).Take(info).Error)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ObjectPutGorm struct {
|
||||
*MetaDB
|
||||
}
|
||||
|
||||
func NewObjectPut(db *gorm.DB) relation.ObjectPutModelInterface {
|
||||
return &ObjectPutGorm{
|
||||
NewMetaDB(db, &relation.ObjectPutModel{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ObjectPutGorm) NewTx(tx any) relation.ObjectPutModelInterface {
|
||||
return &ObjectPutGorm{
|
||||
NewMetaDB(tx.(*gorm.DB), &relation.ObjectPutModel{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ObjectPutGorm) Create(ctx context.Context, m []*relation.ObjectPutModel) (err error) {
|
||||
return utils.Wrap1(o.DB.Create(m).Error)
|
||||
}
|
||||
|
||||
func (o *ObjectPutGorm) Take(ctx context.Context, putID string) (put *relation.ObjectPutModel, err error) {
|
||||
put = &relation.ObjectPutModel{}
|
||||
return put, utils.Wrap1(o.DB.Where("put_id = ?", putID).Take(put).Error)
|
||||
}
|
||||
|
||||
func (o *ObjectPutGorm) SetCompleted(ctx context.Context, putID string) (err error) {
|
||||
return utils.Wrap1(o.DB.Model(&relation.ObjectPutModel{}).Where("put_id = ?", putID).Update("complete", true).Error)
|
||||
}
|
||||
|
||||
func (o *ObjectPutGorm) FindExpirationPut(ctx context.Context, expirationTime time.Time, num int) (list []*relation.ObjectPutModel, err error) {
|
||||
err = o.DB.Where("effective_time <= ?", expirationTime).Limit(num).Find(&list).Error
|
||||
return list, utils.Wrap1(err)
|
||||
}
|
||||
|
||||
func (o *ObjectPutGorm) DelPut(ctx context.Context, ids []string) (err error) {
|
||||
return utils.Wrap1(o.DB.Where("put_id IN ?", ids).Delete(&relation.ObjectPutModel{}).Error)
|
||||
}
|
||||
@@ -1,13 +1,29 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserGorm struct {
|
||||
@@ -25,7 +41,7 @@ func (u *UserGorm) Create(ctx context.Context, users []*relation.UserModel) (err
|
||||
|
||||
// 更新用户信息 零值
|
||||
func (u *UserGorm) UpdateByMap(ctx context.Context, userID string, args map[string]interface{}) (err error) {
|
||||
return utils.Wrap(u.db(ctx).Where("user_id = ?", userID).Updates(args).Error, "")
|
||||
return utils.Wrap(u.db(ctx).Model(&relation.UserModel{}).Where("user_id = ?", userID).Updates(args).Error, "")
|
||||
}
|
||||
|
||||
// 更新多个用户信息 非零值
|
||||
@@ -47,12 +63,23 @@ func (u *UserGorm) Take(ctx context.Context, userID string) (user *relation.User
|
||||
}
|
||||
|
||||
// 获取用户信息 不存在,不返回错误
|
||||
func (u *UserGorm) Page(ctx context.Context, pageNumber, showNumber int32) (users []*relation.UserModel, count int64, err error) {
|
||||
func (u *UserGorm) Page(
|
||||
ctx context.Context,
|
||||
pageNumber, showNumber int32,
|
||||
) (users []*relation.UserModel, count int64, err error) {
|
||||
err = utils.Wrap(u.db(ctx).Count(&count).Error, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = utils.Wrap(u.db(ctx).Limit(int(showNumber)).Offset(int((pageNumber-1)*showNumber)).Find(&users).Order("create_time DESC").Error, "")
|
||||
err = utils.Wrap(
|
||||
u.db(ctx).
|
||||
Limit(int(showNumber)).
|
||||
Offset(int((pageNumber-1)*showNumber)).
|
||||
Find(&users).
|
||||
Order("create_time DESC").
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,17 +94,33 @@ func (u *UserGorm) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (
|
||||
return opt, err
|
||||
}
|
||||
|
||||
func (u *UserGorm) CountTotal(ctx context.Context) (count int64, err error) {
|
||||
err = u.db(ctx).Model(&relation.UserModel{}).Count(&count).Error
|
||||
return count, errs.Wrap(err)
|
||||
func (u *UserGorm) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
||||
db := u.db(ctx).Model(&relation.UserModel{})
|
||||
if before != nil {
|
||||
db = db.Where("create_time < ?", before)
|
||||
}
|
||||
if err := db.Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (u *UserGorm) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
|
||||
func (u *UserGorm) CountRangeEverydayTotal(
|
||||
ctx context.Context,
|
||||
start time.Time,
|
||||
end time.Time,
|
||||
) (map[string]int64, error) {
|
||||
var res []struct {
|
||||
Date time.Time `gorm:"column:date"`
|
||||
Count int64 `gorm:"column:count"`
|
||||
}
|
||||
err := u.db(ctx).Model(&relation.UserModel{}).Select("DATE(create_time) AS date, count(1) AS count").Where("create_time >= ? and create_time < ?", start, end).Group("date").Find(&res).Error
|
||||
err := u.db(ctx).
|
||||
Model(&relation.UserModel{}).
|
||||
Select("DATE(create_time) AS date, count(1) AS count").
|
||||
Where("create_time >= ? and create_time < ?", start, end).
|
||||
Group("date").
|
||||
Find(&res).
|
||||
Error
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package cont
|
||||
|
||||
const (
|
||||
hashPath = "openim/data/hash/"
|
||||
tempPath = "openim/temp/"
|
||||
UploadTypeMultipart = 1 // 分片上传
|
||||
UploadTypePresigned = 2 // 预签名上传
|
||||
partSeparator = ","
|
||||
)
|
||||
@@ -0,0 +1,244 @@
|
||||
package cont
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/google/uuid"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func New(impl s3.Interface) *Controller {
|
||||
return &Controller{impl: impl}
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
impl s3.Interface
|
||||
}
|
||||
|
||||
func (c *Controller) HashPath(md5 string) string {
|
||||
return path.Join(hashPath, md5)
|
||||
}
|
||||
|
||||
func (c *Controller) NowPath() string {
|
||||
now := time.Now()
|
||||
return path.Join(
|
||||
fmt.Sprintf("%04d", now.Year()),
|
||||
fmt.Sprintf("%02d", now.Month()),
|
||||
fmt.Sprintf("%02d", now.Day()),
|
||||
fmt.Sprintf("%02d", now.Hour()),
|
||||
fmt.Sprintf("%02d", now.Minute()),
|
||||
fmt.Sprintf("%02d", now.Second()),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Controller) UUID() string {
|
||||
id := uuid.New()
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
func (c *Controller) PartSize(ctx context.Context, size int64) (int64, error) {
|
||||
return c.impl.PartSize(ctx, size)
|
||||
}
|
||||
|
||||
func (c *Controller) PartLimit() *s3.PartLimit {
|
||||
return c.impl.PartLimit()
|
||||
}
|
||||
|
||||
func (c *Controller) GetHashObject(ctx context.Context, hash string) (*s3.ObjectInfo, error) {
|
||||
return c.impl.StatObject(ctx, c.HashPath(hash))
|
||||
}
|
||||
|
||||
func (c *Controller) InitiateUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*InitiateUploadResult, error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
if size < 0 {
|
||||
return nil, errors.New("invalid size")
|
||||
}
|
||||
if hashBytes, err := hex.DecodeString(hash); err != nil {
|
||||
return nil, err
|
||||
} else if len(hashBytes) != md5.Size {
|
||||
return nil, errors.New("invalid md5")
|
||||
}
|
||||
partSize, err := c.impl.PartSize(ctx, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
partNumber := int(size / partSize)
|
||||
if size%partSize > 0 {
|
||||
partNumber++
|
||||
}
|
||||
if maxParts > 0 && partNumber > 0 && partNumber < maxParts {
|
||||
return nil, errors.New(fmt.Sprintf("too many parts: %d", partNumber))
|
||||
}
|
||||
if info, err := c.impl.StatObject(ctx, c.HashPath(hash)); err == nil {
|
||||
return nil, &HashAlreadyExistsError{Object: info}
|
||||
} else if !c.impl.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
if size <= partSize {
|
||||
// 预签名上传
|
||||
key := path.Join(tempPath, c.NowPath(), fmt.Sprintf("%s_%d_%s.presigned", hash, size, c.UUID()))
|
||||
rawURL, err := c.impl.PresignedPutObject(ctx, key, expire)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &InitiateUploadResult{
|
||||
UploadID: newMultipartUploadID(multipartUploadID{
|
||||
Type: UploadTypePresigned,
|
||||
ID: "",
|
||||
Key: key,
|
||||
Size: size,
|
||||
Hash: hash,
|
||||
}),
|
||||
PartSize: partSize,
|
||||
Sign: &s3.AuthSignResult{
|
||||
Parts: []s3.SignPart{
|
||||
{
|
||||
PartNumber: 1,
|
||||
URL: rawURL,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
} else {
|
||||
// 分片上传
|
||||
upload, err := c.impl.InitiateMultipartUpload(ctx, c.HashPath(hash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if maxParts < 0 {
|
||||
maxParts = partNumber
|
||||
}
|
||||
var authSign *s3.AuthSignResult
|
||||
if maxParts > 0 {
|
||||
partNumbers := make([]int, partNumber)
|
||||
for i := 0; i < maxParts; i++ {
|
||||
partNumbers[i] = i + 1
|
||||
}
|
||||
authSign, err = c.impl.AuthSign(ctx, upload.UploadID, upload.Key, time.Hour*24, partNumbers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &InitiateUploadResult{
|
||||
UploadID: newMultipartUploadID(multipartUploadID{
|
||||
Type: UploadTypeMultipart,
|
||||
ID: upload.UploadID,
|
||||
Key: upload.Key,
|
||||
Size: size,
|
||||
Hash: hash,
|
||||
}),
|
||||
PartSize: partSize,
|
||||
Sign: authSign,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) CompleteUpload(ctx context.Context, uploadID string, partHashs []string) (*UploadResult, error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
upload, err := parseMultipartUploadID(uploadID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if md5Sum := md5.Sum([]byte(strings.Join(partHashs, partSeparator))); hex.EncodeToString(md5Sum[:]) != upload.Hash {
|
||||
fmt.Println("CompleteUpload sum:", hex.EncodeToString(md5Sum[:]), "upload hash:", upload.Hash)
|
||||
return nil, errors.New("md5 mismatching")
|
||||
}
|
||||
if info, err := c.impl.StatObject(ctx, c.HashPath(upload.Hash)); err == nil {
|
||||
return &UploadResult{
|
||||
Key: info.Key,
|
||||
Size: info.Size,
|
||||
Hash: info.ETag,
|
||||
}, nil
|
||||
} else if !c.impl.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
cleanObject := make(map[string]struct{})
|
||||
defer func() {
|
||||
for key := range cleanObject {
|
||||
_ = c.impl.DeleteObject(ctx, key)
|
||||
}
|
||||
}()
|
||||
var targetKey string
|
||||
switch upload.Type {
|
||||
case UploadTypeMultipart:
|
||||
parts := make([]s3.Part, len(partHashs))
|
||||
for i, part := range partHashs {
|
||||
parts[i] = s3.Part{
|
||||
PartNumber: i + 1,
|
||||
ETag: part,
|
||||
}
|
||||
}
|
||||
// todo: 验证大小
|
||||
result, err := c.impl.CompleteMultipartUpload(ctx, upload.ID, upload.Key, parts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
targetKey = result.Key
|
||||
case UploadTypePresigned:
|
||||
uploadInfo, err := c.impl.StatObject(ctx, upload.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cleanObject[uploadInfo.Key] = struct{}{}
|
||||
if uploadInfo.Size != upload.Size {
|
||||
return nil, errors.New("upload size mismatching")
|
||||
}
|
||||
md5Sum := md5.Sum([]byte(strings.Join([]string{uploadInfo.ETag}, partSeparator)))
|
||||
if md5val := hex.EncodeToString(md5Sum[:]); md5val != upload.Hash {
|
||||
return nil, errs.ErrArgs.Wrap(fmt.Sprintf("md5 mismatching %s != %s", md5val, upload.Hash))
|
||||
}
|
||||
// 防止在这个时候,并发操作,导致文件被覆盖
|
||||
copyInfo, err := c.impl.CopyObject(ctx, uploadInfo.Key, upload.Key+"."+c.UUID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cleanObject[copyInfo.Key] = struct{}{}
|
||||
if copyInfo.ETag != uploadInfo.ETag {
|
||||
return nil, errors.New("[concurrency]copy md5 mismatching")
|
||||
}
|
||||
hashCopyInfo, err := c.impl.CopyObject(ctx, copyInfo.Key, c.HashPath(upload.Hash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.ZInfo(ctx, "hashCopyInfo", "value", fmt.Sprintf("%+v", hashCopyInfo))
|
||||
targetKey = hashCopyInfo.Key
|
||||
default:
|
||||
return nil, errors.New("invalid upload id type")
|
||||
}
|
||||
return &UploadResult{
|
||||
Key: targetKey,
|
||||
Size: upload.Size,
|
||||
Hash: upload.Hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) {
|
||||
upload, err := parseMultipartUploadID(uploadID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch upload.Type {
|
||||
case UploadTypeMultipart:
|
||||
return c.impl.AuthSign(ctx, upload.ID, upload.Key, time.Hour*24, partNumbers)
|
||||
case UploadTypePresigned:
|
||||
return nil, errors.New("presigned id not support auth sign")
|
||||
default:
|
||||
return nil, errors.New("invalid upload id type")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) IsNotFound(err error) bool {
|
||||
return c.impl.IsNotFound(err)
|
||||
}
|
||||
|
||||
func (c *Controller) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||
return c.impl.AccessURL(ctx, name, expire, opt)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cont
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||
)
|
||||
|
||||
type HashAlreadyExistsError struct {
|
||||
Object *s3.ObjectInfo
|
||||
}
|
||||
|
||||
func (e *HashAlreadyExistsError) Error() string {
|
||||
return fmt.Sprintf("hash already exists: %s", e.Object.Key)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cont
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type multipartUploadID struct {
|
||||
Type int `json:"a,omitempty"`
|
||||
ID string `json:"b,omitempty"`
|
||||
Key string `json:"c,omitempty"`
|
||||
Size int64 `json:"d,omitempty"`
|
||||
Hash string `json:"e,omitempty"`
|
||||
}
|
||||
|
||||
func newMultipartUploadID(id multipartUploadID) string {
|
||||
data, err := json.Marshal(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
func parseMultipartUploadID(id string) (*multipartUploadID, error) {
|
||||
data, err := base64.StdEncoding.DecodeString(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid multipart upload id: %w", err)
|
||||
}
|
||||
var upload multipartUploadID
|
||||
if err := json.Unmarshal(data, &upload); err != nil {
|
||||
return nil, fmt.Errorf("invalid multipart upload id: %w", err)
|
||||
}
|
||||
return &upload, nil
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cont
|
||||
|
||||
import "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||
|
||||
type InitiateUploadResult struct {
|
||||
UploadID string `json:"uploadID"` // 上传ID
|
||||
PartSize int64 `json:"partSize"` // 分片大小
|
||||
Sign *s3.AuthSignResult `json:"sign"` // 分片信息
|
||||
}
|
||||
|
||||
type UploadResult struct {
|
||||
Hash string `json:"hash"`
|
||||
Size int64 `json:"size"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package cos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||
"github.com/tencentyun/cos-go-sdk-v5"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
minPartSize = 1024 * 1024 * 1 // 1MB
|
||||
maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB
|
||||
maxNumSize = 1000
|
||||
)
|
||||
|
||||
func NewCos() (s3.Interface, error) {
|
||||
conf := config.Config.Object.Cos
|
||||
u, err := url.Parse(conf.BucketURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
client := cos.NewClient(&cos.BaseURL{BucketURL: u}, &http.Client{
|
||||
Transport: &cos.AuthorizationTransport{
|
||||
SecretID: conf.SecretID,
|
||||
SecretKey: conf.SecretKey,
|
||||
SessionToken: conf.SessionToken,
|
||||
},
|
||||
})
|
||||
return &Cos{
|
||||
copyURL: u.Host + "/",
|
||||
client: client,
|
||||
credential: client.GetCredential(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Cos struct {
|
||||
copyURL string
|
||||
client *cos.Client
|
||||
credential *cos.Credential
|
||||
}
|
||||
|
||||
func (c *Cos) Engine() string {
|
||||
return "tencent-cos"
|
||||
}
|
||||
|
||||
func (c *Cos) PartLimit() *s3.PartLimit {
|
||||
return &s3.PartLimit{
|
||||
MinPartSize: minPartSize,
|
||||
MaxPartSize: maxPartSize,
|
||||
MaxNumSize: maxNumSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cos) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) {
|
||||
result, _, err := c.client.Object.InitiateMultipartUpload(ctx, name, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.InitiateMultipartUploadResult{
|
||||
UploadID: result.UploadID,
|
||||
Bucket: result.Bucket,
|
||||
Key: result.Key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Cos) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) {
|
||||
opts := &cos.CompleteMultipartUploadOptions{
|
||||
Parts: make([]cos.Object, len(parts)),
|
||||
}
|
||||
for i, part := range parts {
|
||||
opts.Parts[i] = cos.Object{
|
||||
PartNumber: part.PartNumber,
|
||||
ETag: strings.ReplaceAll(part.ETag, `"`, ``),
|
||||
}
|
||||
}
|
||||
result, _, err := c.client.Object.CompleteMultipartUpload(ctx, name, uploadID, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.CompleteMultipartUploadResult{
|
||||
Location: result.Location,
|
||||
Bucket: result.Bucket,
|
||||
Key: result.Key,
|
||||
ETag: result.ETag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Cos) PartSize(ctx context.Context, size int64) (int64, error) {
|
||||
if size <= 0 {
|
||||
return 0, errors.New("size must be greater than 0")
|
||||
}
|
||||
if size > maxPartSize*maxNumSize {
|
||||
return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize)
|
||||
}
|
||||
if size <= minPartSize*maxNumSize {
|
||||
return minPartSize, nil
|
||||
}
|
||||
partSize := size / maxNumSize
|
||||
if size%maxNumSize != 0 {
|
||||
partSize++
|
||||
}
|
||||
return partSize, nil
|
||||
}
|
||||
|
||||
func (c *Cos) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) {
|
||||
result := s3.AuthSignResult{
|
||||
URL: c.client.BaseURL.BucketURL.String() + "/" + cos.EncodeURIComponent(name),
|
||||
Query: url.Values{"uploadId": {uploadID}},
|
||||
Header: make(http.Header),
|
||||
Parts: make([]s3.SignPart, len(partNumbers)),
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, result.URL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cos.AddAuthorizationHeader(c.credential.SecretID, c.credential.SecretKey, c.credential.SessionToken, req, cos.NewAuthTime(expire))
|
||||
result.Header = req.Header
|
||||
for i, partNumber := range partNumbers {
|
||||
result.Parts[i] = s3.SignPart{
|
||||
PartNumber: partNumber,
|
||||
Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}},
|
||||
}
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (c *Cos) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) {
|
||||
rawURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodPut, name, c.credential.SecretID, c.credential.SecretKey, expire, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rawURL.String(), nil
|
||||
}
|
||||
|
||||
func (c *Cos) DeleteObject(ctx context.Context, name string) error {
|
||||
_, err := c.client.Object.Delete(ctx, name)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cos) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
|
||||
if name != "" && name[0] == '/' {
|
||||
name = name[1:]
|
||||
}
|
||||
info, err := c.client.Object.Head(ctx, name, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &s3.ObjectInfo{Key: name}
|
||||
if res.ETag = strings.ToLower(strings.ReplaceAll(info.Header.Get("ETag"), `"`, "")); res.ETag == "" {
|
||||
return nil, errors.New("StatObject etag not found")
|
||||
}
|
||||
if contentLengthStr := info.Header.Get("Content-Length"); contentLengthStr == "" {
|
||||
return nil, errors.New("StatObject content-length not found")
|
||||
} else {
|
||||
res.Size, err = strconv.ParseInt(contentLengthStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("StatObject content-length parse error: %w", err)
|
||||
}
|
||||
if res.Size < 0 {
|
||||
return nil, errors.New("StatObject content-length must be greater than 0")
|
||||
}
|
||||
}
|
||||
if lastModified := info.Header.Get("Last-Modified"); lastModified == "" {
|
||||
return nil, errors.New("StatObject last-modified not found")
|
||||
} else {
|
||||
res.LastModified, err = time.Parse(http.TimeFormat, lastModified)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("StatObject last-modified parse error: %w", err)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Cos) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) {
|
||||
sourceURL := c.copyURL + src
|
||||
result, _, err := c.client.Object.Copy(ctx, dst, sourceURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.CopyObjectInfo{
|
||||
Key: dst,
|
||||
ETag: strings.ReplaceAll(result.ETag, `"`, ``),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Cos) IsNotFound(err error) bool {
|
||||
switch e := err.(type) {
|
||||
case *cos.ErrorResponse:
|
||||
return e.Response.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cos) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error {
|
||||
_, err := c.client.Object.AbortMultipartUpload(ctx, name, uploadID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cos) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) {
|
||||
result, _, err := c.client.Object.ListParts(ctx, name, uploadID, &cos.ObjectListPartsOptions{
|
||||
MaxParts: strconv.Itoa(maxParts),
|
||||
PartNumberMarker: strconv.Itoa(partNumberMarker),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &s3.ListUploadedPartsResult{
|
||||
Key: result.Key,
|
||||
UploadID: result.UploadID,
|
||||
UploadedParts: make([]s3.UploadedPart, len(result.Parts)),
|
||||
}
|
||||
res.MaxParts, _ = strconv.Atoi(result.MaxParts)
|
||||
res.NextPartNumberMarker, _ = strconv.Atoi(result.NextPartNumberMarker)
|
||||
for i, part := range result.Parts {
|
||||
lastModified, _ := time.Parse(http.TimeFormat, part.LastModified)
|
||||
res.UploadedParts[i] = s3.UploadedPart{
|
||||
PartNumber: part.PartNumber,
|
||||
LastModified: lastModified,
|
||||
ETag: part.ETag,
|
||||
Size: part.Size,
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Cos) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||
//reqParams := make(url.Values)
|
||||
//if opt != nil {
|
||||
// if opt.ContentType != "" {
|
||||
// reqParams.Set("Content-Type", opt.ContentType)
|
||||
// }
|
||||
// if opt.ContentDisposition != "" {
|
||||
// reqParams.Set("Content-Disposition", opt.ContentDisposition)
|
||||
// }
|
||||
//}
|
||||
if expire <= 0 {
|
||||
expire = time.Hour * 24 * 365 * 99 // 99 years
|
||||
} else if expire < time.Second {
|
||||
expire = time.Second
|
||||
}
|
||||
rawURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodGet, name, c.credential.SecretID, c.credential.SecretKey, expire, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rawURL.String(), nil
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package minio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/minio/minio-go/v7/pkg/signer"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
unsignedPayload = "UNSIGNED-PAYLOAD"
|
||||
)
|
||||
|
||||
const (
|
||||
minPartSize = 1024 * 1024 * 5 // 1MB
|
||||
maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB
|
||||
maxNumSize = 10000
|
||||
)
|
||||
|
||||
func NewMinio() (s3.Interface, error) {
|
||||
conf := config.Config.Object.Minio
|
||||
u, err := url.Parse(conf.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts := &minio.Options{
|
||||
Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, conf.SessionToken),
|
||||
Secure: u.Scheme == "https",
|
||||
}
|
||||
client, err := minio.New(u.Host, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Minio{
|
||||
bucket: conf.Bucket,
|
||||
bucketURL: conf.Endpoint + "/" + conf.Bucket + "/",
|
||||
opts: opts,
|
||||
core: &minio.Core{Client: client},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Minio struct {
|
||||
bucket string
|
||||
bucketURL string
|
||||
opts *minio.Options
|
||||
core *minio.Core
|
||||
}
|
||||
|
||||
func (m *Minio) Engine() string {
|
||||
return "minio"
|
||||
}
|
||||
|
||||
func (m *Minio) PartLimit() *s3.PartLimit {
|
||||
return &s3.PartLimit{
|
||||
MinPartSize: minPartSize,
|
||||
MaxPartSize: maxPartSize,
|
||||
MaxNumSize: maxNumSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Minio) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) {
|
||||
uploadID, err := m.core.NewMultipartUpload(ctx, m.bucket, name, minio.PutObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.InitiateMultipartUploadResult{
|
||||
Bucket: m.bucket,
|
||||
Key: name,
|
||||
UploadID: uploadID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Minio) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) {
|
||||
minioParts := make([]minio.CompletePart, len(parts))
|
||||
for i, part := range parts {
|
||||
minioParts[i] = minio.CompletePart{
|
||||
PartNumber: part.PartNumber,
|
||||
ETag: strings.ToLower(part.ETag),
|
||||
}
|
||||
}
|
||||
upload, err := m.core.CompleteMultipartUpload(ctx, m.bucket, name, uploadID, minioParts, minio.PutObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.CompleteMultipartUploadResult{
|
||||
Location: upload.Location,
|
||||
Bucket: upload.Bucket,
|
||||
Key: upload.Key,
|
||||
ETag: strings.ToLower(upload.ETag),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Minio) PartSize(ctx context.Context, size int64) (int64, error) {
|
||||
if size <= 0 {
|
||||
return 0, errors.New("size must be greater than 0")
|
||||
}
|
||||
if size > maxPartSize*maxNumSize {
|
||||
return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize)
|
||||
}
|
||||
if size <= minPartSize*maxNumSize {
|
||||
return minPartSize, nil
|
||||
}
|
||||
partSize := size / maxNumSize
|
||||
if size%maxNumSize != 0 {
|
||||
partSize++
|
||||
}
|
||||
return partSize, nil
|
||||
}
|
||||
|
||||
func (m *Minio) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) {
|
||||
creds, err := m.opts.Creds.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := s3.AuthSignResult{
|
||||
URL: m.bucketURL + name,
|
||||
Query: url.Values{"uploadId": {uploadID}},
|
||||
Parts: make([]s3.SignPart, len(partNumbers)),
|
||||
}
|
||||
for i, partNumber := range partNumbers {
|
||||
rawURL := result.URL + "?partNumber=" + strconv.Itoa(partNumber) + "&uploadId=" + uploadID
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPut, rawURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Set("X-Amz-Content-Sha256", unsignedPayload)
|
||||
request = signer.SignV4Trailer(*request, creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken, "us-east-1", nil)
|
||||
result.Parts[i] = s3.SignPart{
|
||||
PartNumber: partNumber,
|
||||
URL: request.URL.String(),
|
||||
Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}},
|
||||
Header: request.Header,
|
||||
}
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (m *Minio) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) {
|
||||
rawURL, err := m.core.Client.PresignedPutObject(ctx, m.bucket, name, expire)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rawURL.String(), nil
|
||||
}
|
||||
|
||||
func (m *Minio) DeleteObject(ctx context.Context, name string) error {
|
||||
return m.core.Client.RemoveObject(ctx, m.bucket, name, minio.RemoveObjectOptions{})
|
||||
}
|
||||
|
||||
func (m *Minio) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
|
||||
info, err := m.core.Client.StatObject(ctx, m.bucket, name, minio.StatObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.ObjectInfo{
|
||||
ETag: strings.ToLower(info.ETag),
|
||||
Key: info.Key,
|
||||
Size: info.Size,
|
||||
LastModified: info.LastModified,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Minio) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) {
|
||||
result, err := m.core.Client.CopyObject(ctx, minio.CopyDestOptions{
|
||||
Bucket: m.bucket,
|
||||
Object: dst,
|
||||
}, minio.CopySrcOptions{
|
||||
Bucket: m.bucket,
|
||||
Object: src,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.CopyObjectInfo{
|
||||
Key: dst,
|
||||
ETag: strings.ToLower(result.ETag),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Minio) IsNotFound(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
switch e := err.(type) {
|
||||
case minio.ErrorResponse:
|
||||
return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
|
||||
case *minio.ErrorResponse:
|
||||
return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Minio) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error {
|
||||
return m.core.AbortMultipartUpload(ctx, m.bucket, name, uploadID)
|
||||
}
|
||||
|
||||
func (m *Minio) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) {
|
||||
result, err := m.core.ListObjectParts(ctx, m.bucket, name, uploadID, partNumberMarker, maxParts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &s3.ListUploadedPartsResult{
|
||||
Key: result.Key,
|
||||
UploadID: result.UploadID,
|
||||
MaxParts: result.MaxParts,
|
||||
NextPartNumberMarker: result.NextPartNumberMarker,
|
||||
UploadedParts: make([]s3.UploadedPart, len(result.ObjectParts)),
|
||||
}
|
||||
for i, part := range result.ObjectParts {
|
||||
res.UploadedParts[i] = s3.UploadedPart{
|
||||
PartNumber: part.PartNumber,
|
||||
LastModified: part.LastModified,
|
||||
ETag: part.ETag,
|
||||
Size: part.Size,
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (m *Minio) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||
//reqParams := make(url.Values)
|
||||
//if opt != nil {
|
||||
// if opt.ContentType != "" {
|
||||
// reqParams.Set("Content-Type", opt.ContentType)
|
||||
// }
|
||||
// if opt.ContentDisposition != "" {
|
||||
// reqParams.Set("Content-Disposition", opt.ContentDisposition)
|
||||
// }
|
||||
//}
|
||||
if expire <= 0 {
|
||||
expire = time.Hour * 24 * 365 * 99 // 99 years
|
||||
} else if expire < time.Second {
|
||||
expire = time.Second
|
||||
}
|
||||
u, err := m.core.Client.PresignedGetObject(ctx, m.bucket, name, expire, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
minPartSize = 1024 * 1024 * 1 // 1MB
|
||||
maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB
|
||||
maxNumSize = 10000
|
||||
)
|
||||
|
||||
func NewOSS() (s3.Interface, error) {
|
||||
conf := config.Config.Object.Oss
|
||||
if conf.BucketURL == "" {
|
||||
return nil, errors.New("bucket url is empty")
|
||||
}
|
||||
client, err := oss.New(conf.Endpoint, conf.AccessKeyID, conf.AccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bucket, err := client.Bucket(conf.Bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if conf.BucketURL[len(conf.BucketURL)-1] != '/' {
|
||||
conf.BucketURL += "/"
|
||||
}
|
||||
return &OSS{
|
||||
bucketURL: conf.BucketURL,
|
||||
bucket: bucket,
|
||||
credentials: client.Config.GetCredentials(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type OSS struct {
|
||||
bucketURL string
|
||||
bucket *oss.Bucket
|
||||
credentials oss.Credentials
|
||||
}
|
||||
|
||||
func (o *OSS) Engine() string {
|
||||
return "ali-oss"
|
||||
}
|
||||
|
||||
func (o *OSS) PartLimit() *s3.PartLimit {
|
||||
return &s3.PartLimit{
|
||||
MinPartSize: minPartSize,
|
||||
MaxPartSize: maxPartSize,
|
||||
MaxNumSize: maxNumSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *OSS) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) {
|
||||
result, err := o.bucket.InitiateMultipartUpload(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.InitiateMultipartUploadResult{
|
||||
UploadID: result.UploadID,
|
||||
Bucket: result.Bucket,
|
||||
Key: result.Key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *OSS) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) {
|
||||
ossParts := make([]oss.UploadPart, len(parts))
|
||||
for i, part := range parts {
|
||||
ossParts[i] = oss.UploadPart{
|
||||
PartNumber: part.PartNumber,
|
||||
ETag: strings.ToUpper(part.ETag),
|
||||
}
|
||||
}
|
||||
result, err := o.bucket.CompleteMultipartUpload(oss.InitiateMultipartUploadResult{
|
||||
UploadID: uploadID,
|
||||
Bucket: o.bucket.BucketName,
|
||||
Key: name,
|
||||
}, ossParts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.CompleteMultipartUploadResult{
|
||||
Location: result.Location,
|
||||
Bucket: result.Bucket,
|
||||
Key: result.Key,
|
||||
ETag: strings.ToLower(strings.ReplaceAll(result.ETag, `"`, ``)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *OSS) PartSize(ctx context.Context, size int64) (int64, error) {
|
||||
if size <= 0 {
|
||||
return 0, errors.New("size must be greater than 0")
|
||||
}
|
||||
if size > maxPartSize*maxNumSize {
|
||||
return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize)
|
||||
}
|
||||
if size <= minPartSize*maxNumSize {
|
||||
return minPartSize, nil
|
||||
}
|
||||
partSize := size / maxNumSize
|
||||
if size%maxNumSize != 0 {
|
||||
partSize++
|
||||
}
|
||||
return partSize, nil
|
||||
}
|
||||
|
||||
func (o *OSS) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) {
|
||||
result := s3.AuthSignResult{
|
||||
URL: o.bucketURL + name,
|
||||
Query: url.Values{"uploadId": {uploadID}},
|
||||
Header: make(http.Header),
|
||||
Parts: make([]s3.SignPart, len(partNumbers)),
|
||||
}
|
||||
for i, partNumber := range partNumbers {
|
||||
rawURL := fmt.Sprintf(`%s%s?partNumber=%d&uploadId=%s`, o.bucketURL, name, partNumber, uploadID)
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPut, rawURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if o.credentials.GetSecurityToken() != "" {
|
||||
request.Header.Set(oss.HTTPHeaderOssSecurityToken, o.credentials.GetSecurityToken())
|
||||
}
|
||||
request.Header.Set(oss.HTTPHeaderHost, request.Host)
|
||||
request.Header.Set(oss.HTTPHeaderDate, time.Now().UTC().Format(http.TimeFormat))
|
||||
authorization := fmt.Sprintf(`OSS %s:%s`, o.credentials.GetAccessKeyID(), o.getSignedStr(request, fmt.Sprintf(`/%s/%s?partNumber=%d&uploadId=%s`, o.bucket.BucketName, name, partNumber, uploadID), o.credentials.GetAccessKeySecret()))
|
||||
request.Header.Set(oss.HTTPHeaderAuthorization, authorization)
|
||||
result.Parts[i] = s3.SignPart{
|
||||
PartNumber: partNumber,
|
||||
Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}},
|
||||
URL: request.URL.String(),
|
||||
Header: request.Header,
|
||||
}
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (o *OSS) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) {
|
||||
return o.bucket.SignURL(name, http.MethodPut, int64(expire/time.Second))
|
||||
}
|
||||
|
||||
func (o *OSS) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
|
||||
header, err := o.bucket.GetObjectMeta(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &s3.ObjectInfo{Key: name}
|
||||
if res.ETag = strings.ToLower(strings.ReplaceAll(header.Get("ETag"), `"`, ``)); res.ETag == "" {
|
||||
return nil, errors.New("StatObject etag not found")
|
||||
}
|
||||
if contentLengthStr := header.Get("Content-Length"); contentLengthStr == "" {
|
||||
return nil, errors.New("StatObject content-length not found")
|
||||
} else {
|
||||
res.Size, err = strconv.ParseInt(contentLengthStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("StatObject content-length parse error: %w", err)
|
||||
}
|
||||
if res.Size < 0 {
|
||||
return nil, errors.New("StatObject content-length must be greater than 0")
|
||||
}
|
||||
}
|
||||
if lastModified := header.Get("Last-Modified"); lastModified == "" {
|
||||
return nil, errors.New("StatObject last-modified not found")
|
||||
} else {
|
||||
res.LastModified, err = time.Parse(http.TimeFormat, lastModified)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("StatObject last-modified parse error: %w", err)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (o *OSS) DeleteObject(ctx context.Context, name string) error {
|
||||
return o.bucket.DeleteObject(name)
|
||||
}
|
||||
|
||||
func (o *OSS) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) {
|
||||
result, err := o.bucket.CopyObject(src, dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3.CopyObjectInfo{
|
||||
Key: dst,
|
||||
ETag: strings.ToLower(strings.ReplaceAll(result.ETag, `"`, ``)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *OSS) IsNotFound(err error) bool {
|
||||
switch e := err.(type) {
|
||||
case oss.ServiceError:
|
||||
return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
|
||||
case *oss.ServiceError:
|
||||
return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (o *OSS) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error {
|
||||
return o.bucket.AbortMultipartUpload(oss.InitiateMultipartUploadResult{
|
||||
UploadID: uploadID,
|
||||
Key: name,
|
||||
Bucket: o.bucket.BucketName,
|
||||
})
|
||||
}
|
||||
|
||||
func (o *OSS) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) {
|
||||
result, err := o.bucket.ListUploadedParts(oss.InitiateMultipartUploadResult{
|
||||
UploadID: uploadID,
|
||||
Key: name,
|
||||
Bucket: o.bucket.BucketName,
|
||||
}, oss.MaxUploads(100), oss.MaxParts(maxParts), oss.PartNumberMarker(partNumberMarker))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &s3.ListUploadedPartsResult{
|
||||
Key: result.Key,
|
||||
UploadID: result.UploadID,
|
||||
MaxParts: result.MaxParts,
|
||||
UploadedParts: make([]s3.UploadedPart, len(result.UploadedParts)),
|
||||
}
|
||||
res.NextPartNumberMarker, _ = strconv.Atoi(result.NextPartNumberMarker)
|
||||
for i, part := range result.UploadedParts {
|
||||
res.UploadedParts[i] = s3.UploadedPart{
|
||||
PartNumber: part.PartNumber,
|
||||
LastModified: part.LastModified,
|
||||
ETag: part.ETag,
|
||||
Size: int64(part.Size),
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||
//var opts []oss.Option
|
||||
//if opt != nil {
|
||||
// if opt.ContentType != "" {
|
||||
// opts = append(opts, oss.ContentType(opt.ContentType))
|
||||
// }
|
||||
// if opt.ContentDisposition != "" {
|
||||
// opts = append(opts, oss.ContentDisposition(opt.ContentDisposition))
|
||||
// }
|
||||
//}
|
||||
if expire <= 0 {
|
||||
expire = time.Hour * 24 * 365 * 99 // 99 years
|
||||
} else if expire < time.Second {
|
||||
expire = time.Second
|
||||
}
|
||||
return o.bucket.SignURL(name, http.MethodGet, int64(expire/time.Second))
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (o *OSS) getAdditionalHeaderKeys(req *http.Request) ([]string, map[string]string) {
|
||||
var keysList []string
|
||||
keysMap := make(map[string]string)
|
||||
srcKeys := make(map[string]string)
|
||||
|
||||
for k := range req.Header {
|
||||
srcKeys[strings.ToLower(k)] = ""
|
||||
}
|
||||
|
||||
for _, v := range o.bucket.Client.Config.AdditionalHeaders {
|
||||
if _, ok := srcKeys[strings.ToLower(v)]; ok {
|
||||
keysMap[strings.ToLower(v)] = ""
|
||||
}
|
||||
}
|
||||
|
||||
for k := range keysMap {
|
||||
keysList = append(keysList, k)
|
||||
}
|
||||
sort.Strings(keysList)
|
||||
return keysList, keysMap
|
||||
}
|
||||
|
||||
func (o *OSS) getSignedStr(req *http.Request, canonicalizedResource string, keySecret string) string {
|
||||
// Find out the "x-oss-"'s address in header of the request
|
||||
ossHeadersMap := make(map[string]string)
|
||||
additionalList, additionalMap := o.getAdditionalHeaderKeys(req)
|
||||
for k, v := range req.Header {
|
||||
if strings.HasPrefix(strings.ToLower(k), "x-oss-") {
|
||||
ossHeadersMap[strings.ToLower(k)] = v[0]
|
||||
} else if o.bucket.Client.Config.AuthVersion == oss.AuthV2 {
|
||||
if _, ok := additionalMap[strings.ToLower(k)]; ok {
|
||||
ossHeadersMap[strings.ToLower(k)] = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
hs := newHeaderSorter(ossHeadersMap)
|
||||
|
||||
// Sort the ossHeadersMap by the ascending order
|
||||
hs.Sort()
|
||||
|
||||
// Get the canonicalizedOSSHeaders
|
||||
canonicalizedOSSHeaders := ""
|
||||
for i := range hs.Keys {
|
||||
canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
|
||||
}
|
||||
|
||||
// Give other parameters values
|
||||
// when sign URL, date is expires
|
||||
date := req.Header.Get(oss.HTTPHeaderDate)
|
||||
contentType := req.Header.Get(oss.HTTPHeaderContentType)
|
||||
contentMd5 := req.Header.Get(oss.HTTPHeaderContentMD5)
|
||||
|
||||
// default is v1 signature
|
||||
signStr := req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
|
||||
h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret))
|
||||
|
||||
// v2 signature
|
||||
if o.bucket.Client.Config.AuthVersion == oss.AuthV2 {
|
||||
signStr = req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + strings.Join(additionalList, ";") + "\n" + canonicalizedResource
|
||||
h = hmac.New(func() hash.Hash { return sha256.New() }, []byte(keySecret))
|
||||
}
|
||||
_, _ = io.WriteString(h, signStr)
|
||||
signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
return signedStr
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// headerSorter defines the key-value structure for storing the sorted data in signHeader.
|
||||
type headerSorter struct {
|
||||
Keys []string
|
||||
Vals []string
|
||||
}
|
||||
|
||||
// newHeaderSorter is an additional function for function SignHeader.
|
||||
func newHeaderSorter(m map[string]string) *headerSorter {
|
||||
hs := &headerSorter{
|
||||
Keys: make([]string, 0, len(m)),
|
||||
Vals: make([]string, 0, len(m)),
|
||||
}
|
||||
|
||||
for k, v := range m {
|
||||
hs.Keys = append(hs.Keys, k)
|
||||
hs.Vals = append(hs.Vals, v)
|
||||
}
|
||||
return hs
|
||||
}
|
||||
|
||||
// Sort is an additional function for function SignHeader.
|
||||
func (hs *headerSorter) Sort() {
|
||||
sort.Sort(hs)
|
||||
}
|
||||
|
||||
// Len is an additional function for function SignHeader.
|
||||
func (hs *headerSorter) Len() int {
|
||||
return len(hs.Vals)
|
||||
}
|
||||
|
||||
// Less is an additional function for function SignHeader.
|
||||
func (hs *headerSorter) Less(i, j int) bool {
|
||||
return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
|
||||
}
|
||||
|
||||
// Swap is an additional function for function SignHeader.
|
||||
func (hs *headerSorter) Swap(i, j int) {
|
||||
hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i]
|
||||
hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i]
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PartLimit struct {
|
||||
MinPartSize int64 `json:"minPartSize"`
|
||||
MaxPartSize int64 `json:"maxPartSize"`
|
||||
MaxNumSize int `json:"maxNumSize"`
|
||||
}
|
||||
|
||||
type InitiateMultipartUploadResult struct {
|
||||
Bucket string `json:"bucket"`
|
||||
Key string `json:"key"`
|
||||
UploadID string `json:"uploadID"`
|
||||
}
|
||||
|
||||
type MultipartUploadRequest struct {
|
||||
UploadID string `json:"uploadId"`
|
||||
Bucket string `json:"bucket"`
|
||||
Key string `json:"key"`
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
Query url.Values `json:"query"`
|
||||
Header http.Header `json:"header"`
|
||||
PartKey string `json:"partKey"`
|
||||
PartSize int64 `json:"partSize"`
|
||||
FirstPart int `json:"firstPart"`
|
||||
}
|
||||
|
||||
type Part struct {
|
||||
PartNumber int `json:"partNumber"`
|
||||
ETag string `json:"etag"`
|
||||
}
|
||||
|
||||
type CompleteMultipartUploadResult struct {
|
||||
Location string `json:"location"`
|
||||
Bucket string `json:"bucket"`
|
||||
Key string `json:"key"`
|
||||
ETag string `json:"etag"`
|
||||
}
|
||||
|
||||
type SignResult struct {
|
||||
Parts []SignPart `json:"parts"`
|
||||
}
|
||||
|
||||
type ObjectInfo struct {
|
||||
ETag string `json:"etag"`
|
||||
Key string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
LastModified time.Time `json:"lastModified"`
|
||||
}
|
||||
|
||||
type CopyObjectInfo struct {
|
||||
Key string `json:"name"`
|
||||
ETag string `json:"etag"`
|
||||
}
|
||||
|
||||
type SignPart struct {
|
||||
PartNumber int `json:"partNumber"`
|
||||
URL string `json:"url"`
|
||||
Query url.Values `json:"query"`
|
||||
Header http.Header `json:"header"`
|
||||
}
|
||||
|
||||
type AuthSignResult struct {
|
||||
URL string `json:"url"`
|
||||
Query url.Values `json:"query"`
|
||||
Header http.Header `json:"header"`
|
||||
Parts []SignPart `json:"parts"`
|
||||
}
|
||||
|
||||
type InitiateUpload struct {
|
||||
UploadID string `json:"uploadId"`
|
||||
Bucket string `json:"bucket"`
|
||||
Key string `json:"key"`
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
Query url.Values `json:"query"`
|
||||
Header http.Header `json:"header"`
|
||||
PartKey string `json:"partKey"`
|
||||
PartSize int64 `json:"partSize"`
|
||||
FirstPart int `json:"firstPart"`
|
||||
}
|
||||
|
||||
type UploadedPart struct {
|
||||
PartNumber int `json:"partNumber"`
|
||||
LastModified time.Time `json:"lastModified"`
|
||||
ETag string `json:"etag"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type ListUploadedPartsResult struct {
|
||||
Key string `xml:"Key"`
|
||||
UploadID string `xml:"UploadId"`
|
||||
NextPartNumberMarker int `xml:"NextPartNumberMarker"`
|
||||
MaxParts int `xml:"MaxParts"`
|
||||
UploadedParts []UploadedPart `xml:"Part"`
|
||||
}
|
||||
|
||||
type AccessURLOption struct {
|
||||
ContentType string `json:"contentType"`
|
||||
ContentDisposition string `json:"contentDisposition"`
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
Engine() string
|
||||
PartLimit() *PartLimit
|
||||
|
||||
InitiateMultipartUpload(ctx context.Context, name string) (*InitiateMultipartUploadResult, error)
|
||||
CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []Part) (*CompleteMultipartUploadResult, error)
|
||||
|
||||
PartSize(ctx context.Context, size int64) (int64, error)
|
||||
AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*AuthSignResult, error)
|
||||
|
||||
PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error)
|
||||
|
||||
DeleteObject(ctx context.Context, name string) error
|
||||
|
||||
CopyObject(ctx context.Context, src string, dst string) (*CopyObjectInfo, error)
|
||||
|
||||
StatObject(ctx context.Context, name string) (*ObjectInfo, error)
|
||||
|
||||
IsNotFound(err error) bool
|
||||
|
||||
AbortMultipartUpload(ctx context.Context, uploadID string, name string) error
|
||||
ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*ListUploadedPartsResult, error)
|
||||
|
||||
AccessURL(ctx context.Context, name string, expire time.Duration, opt *AccessURLOption) (string, error)
|
||||
}
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -29,6 +43,10 @@ type BlackModelInterface interface {
|
||||
Update(ctx context.Context, blacks []*BlackModel) (err error)
|
||||
Find(ctx context.Context, blacks []*BlackModel) (blackList []*BlackModel, err error)
|
||||
Take(ctx context.Context, ownerUserID, blockUserID string) (black *BlackModel, err error)
|
||||
FindOwnerBlacks(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (blacks []*BlackModel, total int64, err error)
|
||||
FindOwnerBlacks(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (blacks []*BlackModel, total int64, err error)
|
||||
FindBlackUserIDs(ctx context.Context, ownerUserID string) (blackUserIDs []string, err error)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -11,21 +25,21 @@ const (
|
||||
)
|
||||
|
||||
type ChatLogModel struct {
|
||||
ServerMsgID string `gorm:"column:server_msg_id;primary_key;type:char(64)" json:"serverMsgID"`
|
||||
ClientMsgID string `gorm:"column:client_msg_id;type:char(64)" json:"clientMsgID"`
|
||||
SendID string `gorm:"column:send_id;type:char(64);index:send_id,priority:2" json:"sendID"`
|
||||
RecvID string `gorm:"column:recv_id;type:char(64);index:recv_id,priority:2" json:"recvID"`
|
||||
SenderPlatformID int32 `gorm:"column:sender_platform_id" json:"senderPlatformID"`
|
||||
SenderNickname string `gorm:"column:sender_nick_name;type:varchar(255)" json:"senderNickname"`
|
||||
SenderFaceURL string `gorm:"column:sender_face_url;type:varchar(255);" json:"senderFaceURL"`
|
||||
SessionType int32 `gorm:"column:session_type;index:session_type,priority:2;index:session_type_alone" json:"sessionType"`
|
||||
MsgFrom int32 `gorm:"column:msg_from" json:"msgFrom"`
|
||||
ContentType int32 `gorm:"column:content_type;index:content_type,priority:2;index:content_type_alone" json:"contentType"`
|
||||
Content string `gorm:"column:content;type:varchar(3000)" json:"content"`
|
||||
Status int32 `gorm:"column:status" json:"status"`
|
||||
ServerMsgID string `gorm:"column:server_msg_id;primary_key;type:char(64)" json:"serverMsgID"`
|
||||
ClientMsgID string `gorm:"column:client_msg_id;type:char(64)" json:"clientMsgID"`
|
||||
SendID string `gorm:"column:send_id;type:char(64);index:send_id,priority:2" json:"sendID"`
|
||||
RecvID string `gorm:"column:recv_id;type:char(64);index:recv_id,priority:2" json:"recvID"`
|
||||
SenderPlatformID int32 `gorm:"column:sender_platform_id" json:"senderPlatformID"`
|
||||
SenderNickname string `gorm:"column:sender_nick_name;type:varchar(255)" json:"senderNickname"`
|
||||
SenderFaceURL string `gorm:"column:sender_face_url;type:varchar(255);" json:"senderFaceURL"`
|
||||
SessionType int32 `gorm:"column:session_type;index:session_type,priority:2;index:session_type_alone" json:"sessionType"`
|
||||
MsgFrom int32 `gorm:"column:msg_from" json:"msgFrom"`
|
||||
ContentType int32 `gorm:"column:content_type;index:content_type,priority:2;index:content_type_alone" json:"contentType"`
|
||||
Content string `gorm:"column:content;type:varchar(3000)" json:"content"`
|
||||
Status int32 `gorm:"column:status" json:"status"`
|
||||
SendTime time.Time `gorm:"column:send_time;index:sendTime;index:content_type,priority:1;index:session_type,priority:1;index:recv_id,priority:1;index:send_id,priority:1" json:"sendTime"`
|
||||
CreateTime time.Time `gorm:"column:create_time" json:"createTime"`
|
||||
Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
|
||||
CreateTime time.Time `gorm:"column:create_time" json:"createTime"`
|
||||
Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
|
||||
}
|
||||
|
||||
func (ChatLogModel) TableName() string {
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
package relation
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
conversationModelTableName = "conversations"
|
||||
)
|
||||
|
||||
type ConversationModel struct {
|
||||
OwnerUserID string `gorm:"column:owner_user_id;primary_key;type:char(128)" json:"OwnerUserID"`
|
||||
ConversationID string `gorm:"column:conversation_id;primary_key;type:char(128)" json:"conversationID"`
|
||||
ConversationType int32 `gorm:"column:conversation_type" json:"conversationType"`
|
||||
UserID string `gorm:"column:user_id;type:char(64)" json:"userID"`
|
||||
GroupID string `gorm:"column:group_id;type:char(128)" json:"groupID"`
|
||||
RecvMsgOpt int32 `gorm:"column:recv_msg_opt" json:"recvMsgOpt"`
|
||||
IsPinned bool `gorm:"column:is_pinned" json:"isPinned"`
|
||||
IsPrivateChat bool `gorm:"column:is_private_chat" json:"isPrivateChat"`
|
||||
BurnDuration int32 `gorm:"column:burn_duration;default:30" json:"burnDuration"`
|
||||
GroupAtType int32 `gorm:"column:group_at_type" json:"groupAtType"`
|
||||
AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"`
|
||||
Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
|
||||
MaxSeq int64 `gorm:"column:max_seq" json:"maxSeq"`
|
||||
MinSeq int64 `gorm:"column:min_seq" json:"minSeq"`
|
||||
OwnerUserID string `gorm:"column:owner_user_id;primary_key;type:char(128)" json:"OwnerUserID"`
|
||||
ConversationID string `gorm:"column:conversation_id;primary_key;type:char(128)" json:"conversationID"`
|
||||
ConversationType int32 `gorm:"column:conversation_type" json:"conversationType"`
|
||||
UserID string `gorm:"column:user_id;type:char(64)" json:"userID"`
|
||||
GroupID string `gorm:"column:group_id;type:char(128)" json:"groupID"`
|
||||
RecvMsgOpt int32 `gorm:"column:recv_msg_opt" json:"recvMsgOpt"`
|
||||
IsPinned bool `gorm:"column:is_pinned" json:"isPinned"`
|
||||
IsPrivateChat bool `gorm:"column:is_private_chat" json:"isPrivateChat"`
|
||||
BurnDuration int32 `gorm:"column:burn_duration;default:30" json:"burnDuration"`
|
||||
GroupAtType int32 `gorm:"column:group_at_type" json:"groupAtType"`
|
||||
AttachedInfo string `gorm:"column:attached_info;type:varchar(1024)" json:"attachedInfo"`
|
||||
Ex string `gorm:"column:ex;type:varchar(1024)" json:"ex"`
|
||||
MaxSeq int64 `gorm:"column:max_seq" json:"maxSeq"`
|
||||
MinSeq int64 `gorm:"column:min_seq" json:"minSeq"`
|
||||
CreateTime time.Time `gorm:"column:create_time;index:create_time;autoCreateTime"`
|
||||
IsMsgDestruct bool `gorm:"column:is_msg_destruct;default:false"`
|
||||
MsgDestructTime int64 `gorm:"column:msg_destruct_time;default:604800"`
|
||||
LatestMsgDestructTime time.Time `gorm:"column:latest_msg_destruct_time;autoCreateTime"`
|
||||
}
|
||||
|
||||
func (ConversationModel) TableName() string {
|
||||
@@ -44,5 +51,6 @@ type ConversationModelInterface interface {
|
||||
GetAllConversationIDs(ctx context.Context) ([]string, error)
|
||||
GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (hashReadSeqs map[string]int64, err error)
|
||||
GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*ConversationModel, error)
|
||||
GetConversationIDsNeedDestruct(ctx context.Context) ([]*ConversationModel, error)
|
||||
NewTx(tx any) ConversationModelInterface
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -41,11 +55,23 @@ type FriendModelInterface interface {
|
||||
// 获取 owner指定的好友列表 如果有friendUserIDs不存在,也不返回错误
|
||||
FindFriends(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*FriendModel, err error)
|
||||
// 获取哪些人添加了friendUserID 如果有ownerUserIDs不存在,也不返回错误
|
||||
FindReversalFriends(ctx context.Context, friendUserID string, ownerUserIDs []string) (friends []*FriendModel, err error)
|
||||
FindReversalFriends(
|
||||
ctx context.Context,
|
||||
friendUserID string,
|
||||
ownerUserIDs []string,
|
||||
) (friends []*FriendModel, err error)
|
||||
// 获取ownerUserID好友列表 支持翻页
|
||||
FindOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) (friends []*FriendModel, total int64, err error)
|
||||
FindOwnerFriends(
|
||||
ctx context.Context,
|
||||
ownerUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*FriendModel, total int64, err error)
|
||||
// 获取哪些人添加了friendUserID 支持翻页
|
||||
FindInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) (friends []*FriendModel, total int64, err error)
|
||||
FindInWhoseFriends(
|
||||
ctx context.Context,
|
||||
friendUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friends []*FriendModel, total int64, err error)
|
||||
// 获取好友UserID列表
|
||||
FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error)
|
||||
NewTx(tx any) FriendModelInterface
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -36,9 +50,17 @@ type FriendRequestModelInterface interface {
|
||||
Find(ctx context.Context, fromUserID, toUserID string) (friendRequest *FriendRequestModel, err error)
|
||||
Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *FriendRequestModel, err error)
|
||||
// 获取toUserID收到的好友申请列表
|
||||
FindToUserID(ctx context.Context, toUserID string, pageNumber, showNumber int32) (friendRequests []*FriendRequestModel, total int64, err error)
|
||||
FindToUserID(
|
||||
ctx context.Context,
|
||||
toUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friendRequests []*FriendRequestModel, total int64, err error)
|
||||
// 获取fromUserID发出去的好友申请列表
|
||||
FindFromUserID(ctx context.Context, fromUserID string, pageNumber, showNumber int32) (friendRequests []*FriendRequestModel, total int64, err error)
|
||||
FindFromUserID(
|
||||
ctx context.Context,
|
||||
fromUserID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (friendRequests []*FriendRequestModel, total int64, err error)
|
||||
|
||||
NewTx(tx any) FriendRequestModelInterface
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -10,19 +24,19 @@ const (
|
||||
)
|
||||
|
||||
type GroupModel struct {
|
||||
GroupID string `gorm:"column:group_id;primary_key;size:64" json:"groupID" binding:"required"`
|
||||
GroupName string `gorm:"column:name;size:255" json:"groupName"`
|
||||
Notification string `gorm:"column:notification;size:255" json:"notification"`
|
||||
Introduction string `gorm:"column:introduction;size:255" json:"introduction"`
|
||||
FaceURL string `gorm:"column:face_url;size:255" json:"faceURL"`
|
||||
GroupID string `gorm:"column:group_id;primary_key;size:64" json:"groupID" binding:"required"`
|
||||
GroupName string `gorm:"column:name;size:255" json:"groupName"`
|
||||
Notification string `gorm:"column:notification;size:255" json:"notification"`
|
||||
Introduction string `gorm:"column:introduction;size:255" json:"introduction"`
|
||||
FaceURL string `gorm:"column:face_url;size:255" json:"faceURL"`
|
||||
CreateTime time.Time `gorm:"column:create_time;index:create_time;autoCreateTime"`
|
||||
Ex string `gorm:"column:ex" json:"ex;size:1024" json:"ex"`
|
||||
Ex string `gorm:"column:ex" json:"ex;size:1024"`
|
||||
Status int32 `gorm:"column:status"`
|
||||
CreatorUserID string `gorm:"column:creator_user_id;size:64"`
|
||||
GroupType int32 `gorm:"column:group_type"`
|
||||
NeedVerification int32 `gorm:"column:need_verification"`
|
||||
LookMemberInfo int32 `gorm:"column:look_member_info" json:"lookMemberInfo"`
|
||||
ApplyMemberFriend int32 `gorm:"column:apply_member_friend" json:"applyMemberFriend"`
|
||||
LookMemberInfo int32 `gorm:"column:look_member_info" json:"lookMemberInfo"`
|
||||
ApplyMemberFriend int32 `gorm:"column:apply_member_friend" json:"applyMemberFriend"`
|
||||
NotificationUpdateTime time.Time `gorm:"column:notification_update_time"`
|
||||
NotificationUserID string `gorm:"column:notification_user_id;size:64"`
|
||||
}
|
||||
@@ -38,6 +52,14 @@ type GroupModelInterface interface {
|
||||
UpdateStatus(ctx context.Context, groupID string, status int32) (err error)
|
||||
Find(ctx context.Context, groupIDs []string) (groups []*GroupModel, err error)
|
||||
Take(ctx context.Context, groupID string) (group *GroupModel, err error)
|
||||
Search(ctx context.Context, keyword string, pageNumber, showNumber int32) (total uint32, groups []*GroupModel, err error)
|
||||
Search(
|
||||
ctx context.Context,
|
||||
keyword string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, groups []*GroupModel, err error)
|
||||
GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error)
|
||||
// 获取群总数
|
||||
CountTotal(ctx context.Context, before *time.Time) (count int64, err error)
|
||||
// 获取范围内群增量
|
||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -34,11 +48,23 @@ type GroupMemberModelInterface interface {
|
||||
DeleteGroup(ctx context.Context, groupIDs []string) (err error)
|
||||
Update(ctx context.Context, groupID string, userID string, data map[string]any) (err error)
|
||||
UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) (rowsAffected int64, err error)
|
||||
Find(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) (groupMembers []*GroupMemberModel, err error)
|
||||
Find(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
) (groupMembers []*GroupMemberModel, err error)
|
||||
FindMemberUserID(ctx context.Context, groupID string) (userIDs []string, err error)
|
||||
Take(ctx context.Context, groupID string, userID string) (groupMember *GroupMemberModel, err error)
|
||||
TakeOwner(ctx context.Context, groupID string) (groupMember *GroupMemberModel, err error)
|
||||
SearchMember(ctx context.Context, keyword string, groupIDs []string, userIDs []string, roleLevels []int32, pageNumber, showNumber int32) (total uint32, groupList []*GroupMemberModel, err error)
|
||||
SearchMember(
|
||||
ctx context.Context,
|
||||
keyword string,
|
||||
groupIDs []string,
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, groupList []*GroupMemberModel, err error)
|
||||
MapGroupMemberNum(ctx context.Context, groupIDs []string) (count map[string]uint32, err error)
|
||||
FindJoinUserID(ctx context.Context, groupIDs []string) (groupUsers map[string][]string, err error)
|
||||
FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error)
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -33,6 +47,14 @@ type GroupRequestModelInterface interface {
|
||||
Delete(ctx context.Context, groupID string, userID string) (err error)
|
||||
UpdateHandler(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32) (err error)
|
||||
Take(ctx context.Context, groupID string, userID string) (groupRequest *GroupRequestModel, err error)
|
||||
Page(ctx context.Context, userID string, pageNumber, showNumber int32) (total uint32, groups []*GroupRequestModel, err error)
|
||||
PageGroup(ctx context.Context, groupIDs []string, pageNumber, showNumber int32) (total uint32, groups []*GroupRequestModel, err error)
|
||||
Page(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, groups []*GroupRequestModel, err error)
|
||||
PageGroup(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
pageNumber, showNumber int32,
|
||||
) (total uint32, groups []*GroupRequestModel, err error)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ObjectInfoModelTableName = "object"
|
||||
)
|
||||
|
||||
type ObjectModel struct {
|
||||
Name string `gorm:"column:name;primary_key"`
|
||||
UserID string `gorm:"column:user_id"`
|
||||
Hash string `gorm:"column:hash"`
|
||||
Key string `gorm:"column:key"`
|
||||
Size int64 `gorm:"column:size"`
|
||||
ContentType string `gorm:"column:content_type"`
|
||||
Cause string `gorm:"column:cause"`
|
||||
CreateTime time.Time `gorm:"column:create_time"`
|
||||
}
|
||||
|
||||
func (ObjectModel) TableName() string {
|
||||
return ObjectInfoModelTableName
|
||||
}
|
||||
|
||||
type ObjectInfoModelInterface interface {
|
||||
NewTx(tx any) ObjectInfoModelInterface
|
||||
SetObject(ctx context.Context, obj *ObjectModel) error
|
||||
Take(ctx context.Context, name string) (*ObjectModel, error)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ObjectHashModelTableName = "object_hash"
|
||||
)
|
||||
|
||||
type ObjectHashModel struct {
|
||||
Hash string `gorm:"column:hash;primary_key;size:32"`
|
||||
Engine string `gorm:"column:engine;primary_key;size:16"`
|
||||
Size int64 `gorm:"column:size"`
|
||||
Bucket string `gorm:"column:bucket"`
|
||||
Name string `gorm:"column:name"`
|
||||
CreateTime time.Time `gorm:"column:create_time"`
|
||||
}
|
||||
|
||||
func (ObjectHashModel) TableName() string {
|
||||
return ObjectHashModelTableName
|
||||
}
|
||||
|
||||
type ObjectHashModelInterface interface {
|
||||
NewTx(tx any) ObjectHashModelInterface
|
||||
Take(ctx context.Context, hash string, engine string) (*ObjectHashModel, error)
|
||||
Create(ctx context.Context, h []*ObjectHashModel) error
|
||||
DeleteNoCitation(ctx context.Context, engine string, num int) (list []*ObjectHashModel, err error)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ObjectInfoModelTableName = "object_info"
|
||||
)
|
||||
|
||||
type ObjectInfoModel struct {
|
||||
Name string `gorm:"column:name;primary_key"`
|
||||
Hash string `gorm:"column:hash"`
|
||||
ContentType string `gorm:"column:content_type"`
|
||||
ValidTime *time.Time `gorm:"column:valid_time"`
|
||||
CreateTime time.Time `gorm:"column:create_time"`
|
||||
}
|
||||
|
||||
func (ObjectInfoModel) TableName() string {
|
||||
return ObjectInfoModelTableName
|
||||
}
|
||||
|
||||
type ObjectInfoModelInterface interface {
|
||||
NewTx(tx any) ObjectInfoModelInterface
|
||||
SetObject(ctx context.Context, obj *ObjectInfoModel) error
|
||||
Take(ctx context.Context, name string) (*ObjectInfoModel, error)
|
||||
DeleteExpiration(ctx context.Context, expiration time.Time) error
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package relation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ObjectPutModelTableName = "object_put"
|
||||
)
|
||||
|
||||
type ObjectPutModel struct {
|
||||
PutID string `gorm:"column:put_id;primary_key"`
|
||||
Hash string `gorm:"column:hash"`
|
||||
Path string `gorm:"column:path"`
|
||||
Name string `gorm:"column:name"`
|
||||
ContentType string `gorm:"column:content_type"`
|
||||
ObjectSize int64 `gorm:"column:object_size"`
|
||||
FragmentSize int64 `gorm:"column:fragment_size"`
|
||||
PutURLsHash string `gorm:"column:put_urls_hash"`
|
||||
ValidTime *time.Time `gorm:"column:valid_time"`
|
||||
EffectiveTime time.Time `gorm:"column:effective_time"`
|
||||
CreateTime time.Time `gorm:"column:create_time"`
|
||||
}
|
||||
|
||||
func (ObjectPutModel) TableName() string {
|
||||
return ObjectPutModelTableName
|
||||
}
|
||||
|
||||
type ObjectPutModelInterface interface {
|
||||
NewTx(tx any) ObjectPutModelInterface
|
||||
Create(ctx context.Context, m []*ObjectPutModel) error
|
||||
Take(ctx context.Context, putID string) (*ObjectPutModel, error)
|
||||
SetCompleted(ctx context.Context, putID string) error
|
||||
FindExpirationPut(ctx context.Context, expirationTime time.Time, num int) ([]*ObjectPutModel, error)
|
||||
DelPut(ctx context.Context, ids []string) error
|
||||
}
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
@@ -14,7 +28,7 @@ type UserModel struct {
|
||||
Nickname string `gorm:"column:name;size:255"`
|
||||
FaceURL string `gorm:"column:face_url;size:255"`
|
||||
Ex string `gorm:"column:ex;size:1024"`
|
||||
CreateTime time.Time `gorm:"column:create_time;index:create_time; autoCreateTime"`
|
||||
CreateTime time.Time `gorm:"column:create_time;index:create_time;autoCreateTime"`
|
||||
AppMangerLevel int32 `gorm:"column:app_manger_level;default:18"`
|
||||
GlobalRecvMsgOpt int32 `gorm:"column:global_recv_msg_opt"`
|
||||
}
|
||||
@@ -52,7 +66,7 @@ type UserModelInterface interface {
|
||||
GetAllUserID(ctx context.Context) (userIDs []string, err error)
|
||||
GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error)
|
||||
// 获取用户总数
|
||||
CountTotal(ctx context.Context) (count int64, err error)
|
||||
CountTotal(ctx context.Context, before *time.Time) (count int64, err error)
|
||||
// 获取范围内用户增量
|
||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
// Copyright © 2023 OpenIM. 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 relation
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
type BatchUpdateGroupMember struct {
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 unrelation
|
||||
|
||||
type CommonUserModel struct {
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package unrelation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
)
|
||||
|
||||
const (
|
||||
CExtendMsgSet = "extend_msgs"
|
||||
|
||||
ExtendMsgMaxNum = 100
|
||||
)
|
||||
|
||||
type ExtendMsgSetModel struct {
|
||||
ConversationID string `bson:"source_id" json:"conversationID"`
|
||||
SessionType int32 `bson:"session_type" json:"sessionType"`
|
||||
ExtendMsgs map[string]ExtendMsgModel `bson:"extend_msgs" json:"extendMsgs"`
|
||||
ExtendMsgNum int32 `bson:"extend_msg_num" json:"extendMsgNum"`
|
||||
CreateTime int64 `bson:"create_time" json:"createTime"` // this block's create time
|
||||
MaxMsgUpdateTime int64 `bson:"max_msg_update_time" json:"maxMsgUpdateTime"` // index find msg
|
||||
}
|
||||
|
||||
type KeyValueModel struct {
|
||||
TypeKey string `bson:"type_key" json:"typeKey"`
|
||||
Value string `bson:"value" json:"value"`
|
||||
LatestUpdateTime int64 `bson:"latest_update_time" json:"latestUpdateTime"`
|
||||
}
|
||||
|
||||
type ExtendMsgModel struct {
|
||||
ReactionExtensionList map[string]KeyValueModel `bson:"reaction_extension_list" json:"reactionExtensionList"`
|
||||
ClientMsgID string `bson:"client_msg_id" json:"clientMsgID"`
|
||||
MsgFirstModifyTime int64 `bson:"msg_first_modify_time" json:"msgFirstModifyTime"` // this extendMsg create time
|
||||
AttachedInfo string `bson:"attached_info" json:"attachedInfo"`
|
||||
Ex string `bson:"ex" json:"ex"`
|
||||
}
|
||||
|
||||
type ExtendMsgSetModelInterface interface {
|
||||
CreateExtendMsgSet(ctx context.Context, set *ExtendMsgSetModel) error
|
||||
GetAllExtendMsgSet(ctx context.Context, conversationID string, opts *GetAllExtendMsgSetOpts) (sets []*ExtendMsgSetModel, err error)
|
||||
GetExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, maxMsgUpdateTime int64) (*ExtendMsgSetModel, error)
|
||||
InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *ExtendMsgModel) error
|
||||
InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*KeyValueModel) error
|
||||
DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*KeyValueModel) error
|
||||
TakeExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (extendMsg *ExtendMsgModel, err error)
|
||||
}
|
||||
|
||||
func (ExtendMsgSetModel) TableName() string {
|
||||
return CExtendMsgSet
|
||||
}
|
||||
|
||||
func (ExtendMsgSetModel) GetExtendMsgMaxNum() int32 {
|
||||
return ExtendMsgMaxNum
|
||||
}
|
||||
|
||||
func (ExtendMsgSetModel) GetConversationID(ID string, index int32) string {
|
||||
return ID + ":" + strconv.Itoa(int(index))
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetModel) SplitConversationIDAndGetIndex() int32 {
|
||||
l := strings.Split(e.ConversationID, ":")
|
||||
index, _ := strconv.Atoi(l[len(l)-1])
|
||||
return int32(index)
|
||||
}
|
||||
|
||||
type GetAllExtendMsgSetOpts struct {
|
||||
ExcludeExtendMsgs bool
|
||||
}
|
||||
|
||||
func (ExtendMsgSetModel) Pb2Model(reactionExtensionList map[string]*sdkws.KeyValue) map[string]*KeyValueModel {
|
||||
r := make(map[string]*KeyValueModel)
|
||||
for key, value := range reactionExtensionList {
|
||||
r[key] = &KeyValueModel{
|
||||
TypeKey: value.TypeKey,
|
||||
Value: value.Value,
|
||||
LatestUpdateTime: value.LatestUpdateTime,
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -1,11 +1,27 @@
|
||||
// Copyright © 2023 OpenIM. 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 unrelation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -21,7 +37,6 @@ type MsgDocModel struct {
|
||||
}
|
||||
|
||||
type RevokeModel struct {
|
||||
ID string `bson:"id"`
|
||||
Role int32 `bson:"role"`
|
||||
UserID string `bson:"user_id"`
|
||||
Nickname string `bson:"nickname"`
|
||||
@@ -68,6 +83,16 @@ type MsgInfoModel struct {
|
||||
IsRead bool `bson:"is_read"`
|
||||
}
|
||||
|
||||
type UserCount struct {
|
||||
UserID string `bson:"user_id"`
|
||||
Count int64 `bson:"count"`
|
||||
}
|
||||
|
||||
type GroupCount struct {
|
||||
GroupID string `bson:"group_id"`
|
||||
Count int64 `bson:"count"`
|
||||
}
|
||||
|
||||
type MsgDocModelInterface interface {
|
||||
PushMsgsToDoc(ctx context.Context, docID string, msgsToMongo []MsgInfoModel) error
|
||||
Create(ctx context.Context, model *MsgDocModel) error
|
||||
@@ -83,6 +108,8 @@ type MsgDocModelInterface interface {
|
||||
GetMsgDocModelByIndex(ctx context.Context, conversationID string, index, sort int64) (*MsgDocModel, error)
|
||||
DeleteMsgsInOneDocByIndex(ctx context.Context, docID string, indexes []int) error
|
||||
MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error
|
||||
RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*UserCount, dateCount map[string]int64, err error)
|
||||
RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*GroupCount, dateCount map[string]int64, err error)
|
||||
}
|
||||
|
||||
func (MsgDocModel) TableName() string {
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 unrelation
|
||||
|
||||
import (
|
||||
@@ -10,7 +24,7 @@ const (
|
||||
)
|
||||
|
||||
type SuperGroupModel struct {
|
||||
GroupID string `bson:"group_id" json:"groupID"`
|
||||
GroupID string `bson:"group_id" json:"groupID"`
|
||||
MemberIDs []string `bson:"member_id_list" json:"memberIDList"`
|
||||
}
|
||||
|
||||
@@ -19,7 +33,7 @@ func (SuperGroupModel) TableName() string {
|
||||
}
|
||||
|
||||
type UserToSuperGroupModel struct {
|
||||
UserID string `bson:"user_id" json:"userID"`
|
||||
UserID string `bson:"user_id" json:"userID"`
|
||||
GroupIDs []string `bson:"group_id_list" json:"groupIDList"`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 tx
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
// Copyright © 2023 OpenIM. 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 tx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
func NewMongo(client *mongo.Client) CtxTx {
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 tx
|
||||
|
||||
import "context"
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
package unrelation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type ExtendMsgSetMongoDriver struct {
|
||||
mgoDB *mongo.Database
|
||||
ExtendMsgSetCollection *mongo.Collection
|
||||
}
|
||||
|
||||
func NewExtendMsgSetMongoDriver(mgoDB *mongo.Database) unRelationTb.ExtendMsgSetModelInterface {
|
||||
return &ExtendMsgSetMongoDriver{mgoDB: mgoDB, ExtendMsgSetCollection: mgoDB.Collection(unRelationTb.CExtendMsgSet)}
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetMongoDriver) CreateExtendMsgSet(ctx context.Context, set *unRelationTb.ExtendMsgSetModel) error {
|
||||
_, err := e.ExtendMsgSetCollection.InsertOne(ctx, set)
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetMongoDriver) GetAllExtendMsgSet(ctx context.Context, ID string, opts *unRelationTb.GetAllExtendMsgSetOpts) (sets []*unRelationTb.ExtendMsgSetModel, err error) {
|
||||
regex := fmt.Sprintf("^%s", ID)
|
||||
var findOpts *options.FindOptions
|
||||
if opts != nil {
|
||||
if opts.ExcludeExtendMsgs {
|
||||
findOpts = &options.FindOptions{}
|
||||
findOpts.SetProjection(bson.M{"extend_msgs": 0})
|
||||
}
|
||||
}
|
||||
cursor, err := e.ExtendMsgSetCollection.Find(ctx, bson.M{"doc_id": primitive.Regex{Pattern: regex}}, findOpts)
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
err = cursor.All(ctx, &sets)
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, fmt.Sprintf("cursor is %s", cursor.Current.String()))
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetMongoDriver) GetExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, maxMsgUpdateTime int64) (*unRelationTb.ExtendMsgSetModel, error) {
|
||||
var err error
|
||||
findOpts := options.Find().SetLimit(1).SetSkip(0).SetSort(bson.M{"source_id": -1}).SetProjection(bson.M{"extend_msgs": 0})
|
||||
// update newest
|
||||
find := bson.M{"source_id": primitive.Regex{Pattern: fmt.Sprintf("^%s", conversationID)}, "session_type": sessionType}
|
||||
if maxMsgUpdateTime > 0 {
|
||||
find["max_msg_update_time"] = maxMsgUpdateTime
|
||||
}
|
||||
result, err := e.ExtendMsgSetCollection.Find(ctx, find, findOpts)
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
var setList []unRelationTb.ExtendMsgSetModel
|
||||
if err := result.All(ctx, &setList); err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
if len(setList) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return &setList[0], nil
|
||||
}
|
||||
|
||||
// first modify msg
|
||||
func (e *ExtendMsgSetMongoDriver) InsertExtendMsg(ctx context.Context, conversationID string, sessionType int32, msg *unRelationTb.ExtendMsgModel) error {
|
||||
set, err := e.GetExtendMsgSet(ctx, conversationID, sessionType, 0)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
if set == nil || set.ExtendMsgNum >= set.GetExtendMsgMaxNum() {
|
||||
var index int32
|
||||
if set != nil {
|
||||
index = set.SplitConversationIDAndGetIndex()
|
||||
}
|
||||
err = e.CreateExtendMsgSet(ctx, &unRelationTb.ExtendMsgSetModel{
|
||||
ConversationID: set.GetConversationID(conversationID, index),
|
||||
SessionType: sessionType,
|
||||
ExtendMsgs: map[string]unRelationTb.ExtendMsgModel{msg.ClientMsgID: *msg},
|
||||
ExtendMsgNum: 1,
|
||||
CreateTime: msg.MsgFirstModifyTime,
|
||||
MaxMsgUpdateTime: msg.MsgFirstModifyTime,
|
||||
})
|
||||
} else {
|
||||
_, err = e.ExtendMsgSetCollection.UpdateOne(ctx, bson.M{"conversation_id": set.ConversationID, "session_type": sessionType}, bson.M{"$set": bson.M{"max_msg_update_time": msg.MsgFirstModifyTime, "$inc": bson.M{"extend_msg_num": 1}, fmt.Sprintf("extend_msgs.%s", msg.ClientMsgID): msg}})
|
||||
}
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
|
||||
// insert or update
|
||||
func (e *ExtendMsgSetMongoDriver) InsertOrUpdateReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error {
|
||||
var updateBson = bson.M{}
|
||||
for _, v := range reactionExtensionList {
|
||||
updateBson[fmt.Sprintf("extend_msgs.%s.%s", clientMsgID, v.TypeKey)] = v
|
||||
}
|
||||
upsert := true
|
||||
opt := &options.UpdateOptions{
|
||||
Upsert: &upsert,
|
||||
}
|
||||
set, err := e.GetExtendMsgSet(ctx, conversationID, sessionType, msgFirstModifyTime)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
if set == nil {
|
||||
return errors.New(fmt.Sprintf("conversationID %s has no set", conversationID))
|
||||
}
|
||||
_, err = e.ExtendMsgSetCollection.UpdateOne(ctx, bson.M{"source_id": set.ConversationID, "session_type": sessionType}, bson.M{"$set": updateBson}, opt)
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
|
||||
// delete TypeKey
|
||||
func (e *ExtendMsgSetMongoDriver) DeleteReactionExtendMsgSet(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, msgFirstModifyTime int64, reactionExtensionList map[string]*unRelationTb.KeyValueModel) error {
|
||||
var updateBson = bson.M{}
|
||||
for _, v := range reactionExtensionList {
|
||||
updateBson[fmt.Sprintf("extend_msgs.%s.%s", clientMsgID, v.TypeKey)] = ""
|
||||
}
|
||||
set, err := e.GetExtendMsgSet(ctx, conversationID, sessionType, msgFirstModifyTime)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
if set == nil {
|
||||
return errors.New(fmt.Sprintf("conversationID %s has no set", conversationID))
|
||||
}
|
||||
_, err = e.ExtendMsgSetCollection.UpdateOne(ctx, bson.M{"source_id": set.ConversationID, "session_type": sessionType}, bson.M{"$unset": updateBson})
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *ExtendMsgSetMongoDriver) TakeExtendMsg(ctx context.Context, conversationID string, sessionType int32, clientMsgID string, maxMsgUpdateTime int64) (extendMsg *unRelationTb.ExtendMsgModel, err error) {
|
||||
findOpts := options.Find().SetLimit(1).SetSkip(0).SetSort(bson.M{"source_id": -1}).SetProjection(bson.M{fmt.Sprintf("extend_msgs.%s", clientMsgID): 1})
|
||||
regex := fmt.Sprintf("^%s", conversationID)
|
||||
result, err := e.ExtendMsgSetCollection.Find(ctx, bson.M{"source_id": primitive.Regex{Pattern: regex}, "session_type": sessionType, "max_msg_update_time": bson.M{"$lte": maxMsgUpdateTime}}, findOpts)
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
var setList []unRelationTb.ExtendMsgSetModel
|
||||
if err := result.All(ctx, &setList); err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
if len(setList) == 0 {
|
||||
return nil, utils.Wrap(errors.New("GetExtendMsg failed, len(setList) == 0"), "")
|
||||
}
|
||||
if v, ok := setList[0].ExtendMsgs[clientMsgID]; ok {
|
||||
return &v, nil
|
||||
}
|
||||
return nil, errors.New(fmt.Sprintf("cant find client msg id: %s", clientMsgID))
|
||||
}
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 unrelation
|
||||
|
||||
import (
|
||||
@@ -6,14 +20,15 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"go.mongodb.org/mongo-driver/x/bsonx"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/mw/specialerror"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"go.mongodb.org/mongo-driver/x/bsonx"
|
||||
)
|
||||
|
||||
type Mongo struct {
|
||||
@@ -24,7 +39,8 @@ func NewMongo() (*Mongo, error) {
|
||||
specialerror.AddReplace(mongo.ErrNoDocuments, errs.ErrRecordNotFound)
|
||||
uri := "mongodb://sample.host:27017/?maxPoolSize=20&w=majority"
|
||||
if config.Config.Mongo.Uri != "" {
|
||||
// example: mongodb://$user:$password@mongo1.mongo:27017,mongo2.mongo:27017,mongo3.mongo:27017/$DBDatabase/?replicaSet=rs0&readPreference=secondary&authSource=admin&maxPoolSize=$DBMaxPoolSize
|
||||
// example:
|
||||
// mongodb://$user:$password@mongo1.mongo:27017,mongo2.mongo:27017,mongo3.mongo:27017/$DBDatabase/?replicaSet=rs0&readPreference=secondary&authSource=admin&maxPoolSize=$DBMaxPoolSize
|
||||
uri = config.Config.Mongo.Uri
|
||||
} else {
|
||||
//mongodb://mongodb1.example.com:27317,mongodb2.example.com:27017/?replicaSet=mySet&authSource=authDB
|
||||
@@ -78,10 +94,6 @@ func (m *Mongo) CreateSuperGroupIndex() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mongo) CreateExtendMsgSetIndex() error {
|
||||
return m.createMongoIndex(unrelation.CExtendMsgSet, true, "-create_time", "work_moment_id")
|
||||
}
|
||||
|
||||
func (m *Mongo) createMongoIndex(collection string, isUnique bool, keys ...string) error {
|
||||
db := m.db.Database(config.Config.Mongo.Database).Collection(collection)
|
||||
opts := options.CreateIndexes().SetMaxTime(10 * time.Second)
|
||||
|
||||
+756
-15
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 unrelation
|
||||
|
||||
import (
|
||||
@@ -5,18 +19,20 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
|
||||
table "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
table "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
var ErrMsgListNotExist = errors.New("user not have msg in mongoDB")
|
||||
@@ -32,7 +48,8 @@ func NewMsgMongoDriver(database *mongo.Database) table.MsgDocModelInterface {
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) PushMsgsToDoc(ctx context.Context, docID string, msgsToMongo []table.MsgInfoModel) error {
|
||||
return m.MsgCollection.FindOneAndUpdate(ctx, bson.M{"doc_id": docID}, bson.M{"$push": bson.M{"msgs": bson.M{"$each": msgsToMongo}}}).Err()
|
||||
return m.MsgCollection.FindOneAndUpdate(ctx, bson.M{"doc_id": docID}, bson.M{"$push": bson.M{"msgs": bson.M{"$each": msgsToMongo}}}).
|
||||
Err()
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) Create(ctx context.Context, model *table.MsgDocModel) error {
|
||||
@@ -40,7 +57,13 @@ func (m *MsgMongoDriver) Create(ctx context.Context, model *table.MsgDocModel) e
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) UpdateMsg(ctx context.Context, docID string, index int64, key string, value any) (*mongo.UpdateResult, error) {
|
||||
func (m *MsgMongoDriver) UpdateMsg(
|
||||
ctx context.Context,
|
||||
docID string,
|
||||
index int64,
|
||||
key string,
|
||||
value any,
|
||||
) (*mongo.UpdateResult, error) {
|
||||
var field string
|
||||
if key == "" {
|
||||
field = fmt.Sprintf("msgs.%d", index)
|
||||
@@ -57,7 +80,13 @@ func (m *MsgMongoDriver) UpdateMsg(ctx context.Context, docID string, index int6
|
||||
}
|
||||
|
||||
// PushUnique value must slice
|
||||
func (m *MsgMongoDriver) PushUnique(ctx context.Context, docID string, index int64, key string, value any) (*mongo.UpdateResult, error) {
|
||||
func (m *MsgMongoDriver) PushUnique(
|
||||
ctx context.Context,
|
||||
docID string,
|
||||
index int64,
|
||||
key string,
|
||||
value any,
|
||||
) (*mongo.UpdateResult, error) {
|
||||
var field string
|
||||
if key == "" {
|
||||
field = fmt.Sprintf("msgs.%d", index)
|
||||
@@ -78,20 +107,34 @@ func (m *MsgMongoDriver) PushUnique(ctx context.Context, docID string, index int
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) UpdateMsgContent(ctx context.Context, docID string, index int64, msg []byte) error {
|
||||
_, err := m.MsgCollection.UpdateOne(ctx, bson.M{"doc_id": docID}, bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", index): msg}})
|
||||
_, err := m.MsgCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"doc_id": docID},
|
||||
bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", index): msg}},
|
||||
)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc(ctx context.Context, docID string, msg *sdkws.MsgData, seqIndex int, status int32) error {
|
||||
func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc(
|
||||
ctx context.Context,
|
||||
docID string,
|
||||
msg *sdkws.MsgData,
|
||||
seqIndex int,
|
||||
status int32,
|
||||
) error {
|
||||
msg.Status = status
|
||||
bytes, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
_, err = m.MsgCollection.UpdateOne(ctx, bson.M{"doc_id": docID}, bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", seqIndex): bytes}})
|
||||
_, err = m.MsgCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"doc_id": docID},
|
||||
bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", seqIndex): bytes}},
|
||||
)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
@@ -104,12 +147,20 @@ func (m *MsgMongoDriver) FindOneByDocID(ctx context.Context, docID string) (*tab
|
||||
return doc, err
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) GetMsgDocModelByIndex(ctx context.Context, conversationID string, index, sort int64) (*table.MsgDocModel, error) {
|
||||
func (m *MsgMongoDriver) GetMsgDocModelByIndex(
|
||||
ctx context.Context,
|
||||
conversationID string,
|
||||
index, sort int64,
|
||||
) (*table.MsgDocModel, error) {
|
||||
if sort != 1 && sort != -1 {
|
||||
return nil, errs.ErrArgs.Wrap("mongo sort must be 1 or -1")
|
||||
}
|
||||
findOpts := options.Find().SetLimit(1).SetSkip(index).SetSort(bson.M{"doc_id": sort})
|
||||
cursor, err := m.MsgCollection.Find(ctx, bson.M{"doc_id": primitive.Regex{Pattern: fmt.Sprintf("^%s:", conversationID)}}, findOpts)
|
||||
cursor, err := m.MsgCollection.Find(
|
||||
ctx,
|
||||
bson.M{"doc_id": primitive.Regex{Pattern: fmt.Sprintf("^%s:", conversationID)}},
|
||||
findOpts,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
@@ -180,7 +231,12 @@ func (m *MsgMongoDriver) DeleteDocs(ctx context.Context, docIDs []string) error
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc(ctx context.Context, userID string, docID string, seqs []int64) (msgs []*table.MsgInfoModel, err error) {
|
||||
func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
docID string,
|
||||
seqs []int64,
|
||||
) (msgs []*table.MsgInfoModel, err error) {
|
||||
indexs := make([]int64, 0, len(seqs))
|
||||
for _, seq := range seqs {
|
||||
indexs = append(indexs, m.model.GetMsgIndex(seq))
|
||||
@@ -247,7 +303,7 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc(ctx context.Context, userID stri
|
||||
}
|
||||
if msg.Revoke != nil {
|
||||
revokeContent := sdkws.MessageRevokedContent{
|
||||
RevokerID: msg.Revoke.ID,
|
||||
RevokerID: msg.Revoke.UserID,
|
||||
RevokerRole: msg.Revoke.Role,
|
||||
ClientMsgID: msg.Msg.ClientMsgID,
|
||||
RevokerNickname: msg.Revoke.Nickname,
|
||||
@@ -286,7 +342,12 @@ func (m *MsgMongoDriver) IsExistDocID(ctx context.Context, docID string) (bool,
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error {
|
||||
func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
docID string,
|
||||
indexes []int64,
|
||||
) error {
|
||||
updates := []mongo.WriteModel{}
|
||||
for _, index := range indexes {
|
||||
filter := bson.M{
|
||||
@@ -308,3 +369,683 @@ func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead(ctx context.Context, userID st
|
||||
_, err := m.MsgCollection.BulkWrite(ctx, updates)
|
||||
return err
|
||||
}
|
||||
|
||||
// RangeUserSendCount
|
||||
// db.msg.aggregate([
|
||||
//
|
||||
// {
|
||||
// $match: {
|
||||
// "msgs.msg.send_time": {
|
||||
// "$gte": 0,
|
||||
// "$lt": 1788122092317
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$addFields": {
|
||||
// "msgs": {
|
||||
// "$filter": {
|
||||
// "input": "$msgs",
|
||||
// "as": "item",
|
||||
// "cond": {
|
||||
// "$and": [
|
||||
// {
|
||||
// $gte: ["$$item.msg.send_time", 0]
|
||||
// },
|
||||
// {
|
||||
// $lt: ["$$item.msg.send_time", 1788122092317]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$project": {
|
||||
// "_id": 0,
|
||||
//
|
||||
// },
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// "$project": {
|
||||
// "result": {
|
||||
// "$map": {
|
||||
// "input": "$msgs",
|
||||
// "as": "item",
|
||||
// "in": {
|
||||
// user_id: "$$item.msg.send_id",
|
||||
// send_date: {
|
||||
// $dateToString: {
|
||||
// format: "%Y-%m-%d",
|
||||
// date: {
|
||||
// $toDate: "$$item.msg.send_time"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// "$unwind": "$result"
|
||||
// },
|
||||
// {
|
||||
// "$group": {
|
||||
// _id: "$result.send_date",
|
||||
// count: {
|
||||
// $sum: 1
|
||||
// },
|
||||
// original: {
|
||||
// $push: "$$ROOT"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$addFields": {
|
||||
// "dates": "$$ROOT"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$project": {
|
||||
// "_id": 0,
|
||||
// "count": 0,
|
||||
// "dates.original": 0,
|
||||
//
|
||||
// },
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// "$group": {
|
||||
// _id: null,
|
||||
// count: {
|
||||
// $sum: 1
|
||||
// },
|
||||
// dates: {
|
||||
// $push: "$dates"
|
||||
// },
|
||||
// original: {
|
||||
// $push: "$original"
|
||||
// },
|
||||
//
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$unwind": "$original"
|
||||
// },
|
||||
// {
|
||||
// "$unwind": "$original"
|
||||
// },
|
||||
// {
|
||||
// "$group": {
|
||||
// _id: "$original.result.user_id",
|
||||
// count: {
|
||||
// $sum: 1
|
||||
// },
|
||||
// original: {
|
||||
// $push: "$dates"
|
||||
// },
|
||||
//
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$addFields": {
|
||||
// "dates": {
|
||||
// $arrayElemAt: ["$original", 0]
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$project": {
|
||||
// original: 0
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// $sort: {
|
||||
// count: - 1
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$group": {
|
||||
// _id: null,
|
||||
// user_count: {
|
||||
// $sum: 1
|
||||
// },
|
||||
// users: {
|
||||
// $push: "$$ROOT"
|
||||
// },
|
||||
//
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$addFields": {
|
||||
// "dates": {
|
||||
// $arrayElemAt: ["$users", 0]
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$addFields": {
|
||||
// "dates": "$dates.dates"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$project": {
|
||||
// _id: 0,
|
||||
// "users.dates": 0,
|
||||
//
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$addFields": {
|
||||
// "msg_count": {
|
||||
// $sum: "$users.count"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "$addFields": {
|
||||
// users: {
|
||||
// $slice: ["$users", 0, 10]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ])
|
||||
func (m *MsgMongoDriver) RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*table.UserCount, dateCount map[string]int64, err error) {
|
||||
var sort int
|
||||
if ase {
|
||||
sort = 1
|
||||
} else {
|
||||
sort = -1
|
||||
}
|
||||
type Result struct {
|
||||
MsgCount int64 `bson:"msg_count"`
|
||||
UserCount int64 `bson:"user_count"`
|
||||
Users []struct {
|
||||
UserID string `bson:"_id"`
|
||||
Count int64 `bson:"count"`
|
||||
} `bson:"users"`
|
||||
Dates []struct {
|
||||
Date string `bson:"_id"`
|
||||
Count int64 `bson:"count"`
|
||||
} `bson:"dates"`
|
||||
}
|
||||
or := bson.A{
|
||||
bson.M{
|
||||
"doc_id": bson.M{
|
||||
"$regex": "^si_",
|
||||
"$options": "i",
|
||||
},
|
||||
},
|
||||
}
|
||||
if group {
|
||||
or = append(or,
|
||||
bson.M{
|
||||
"doc_id": bson.M{
|
||||
"$regex": "^g_",
|
||||
"$options": "i",
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"doc_id": bson.M{
|
||||
"$regex": "^sg_",
|
||||
"$options": "i",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
pipeline := bson.A{
|
||||
bson.M{
|
||||
"$match": bson.M{
|
||||
"$and": bson.A{
|
||||
bson.M{
|
||||
"msgs.msg.send_time": bson.M{
|
||||
"$gte": start.UnixMilli(),
|
||||
"$lt": end.UnixMilli(),
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$or": or,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"msgs": bson.M{
|
||||
"$filter": bson.M{
|
||||
"input": "$msgs",
|
||||
"as": "item",
|
||||
"cond": bson.M{
|
||||
"$and": bson.A{
|
||||
bson.M{
|
||||
"$gte": bson.A{
|
||||
"$$item.msg.send_time", start.UnixMilli(),
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$lt": bson.A{
|
||||
"$$item.msg.send_time", end.UnixMilli(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"_id": 0,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"result": bson.M{
|
||||
"$map": bson.M{
|
||||
"input": "$msgs",
|
||||
"as": "item",
|
||||
"in": bson.M{
|
||||
"user_id": "$$item.msg.send_id",
|
||||
"send_date": bson.M{
|
||||
"$dateToString": bson.M{
|
||||
"format": "%Y-%m-%d",
|
||||
"date": bson.M{
|
||||
"$toDate": "$$item.msg.send_time", // 毫秒时间戳
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$unwind": "$result",
|
||||
},
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": "$result.send_date",
|
||||
"count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"original": bson.M{
|
||||
"$push": "$$ROOT",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"dates": "$$ROOT",
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"_id": 0,
|
||||
"count": 0,
|
||||
"dates.original": 0,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": nil,
|
||||
"count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"dates": bson.M{
|
||||
"$push": "$dates",
|
||||
},
|
||||
"original": bson.M{
|
||||
"$push": "$original",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$unwind": "$original",
|
||||
},
|
||||
bson.M{
|
||||
"$unwind": "$original",
|
||||
},
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": "$original.result.user_id",
|
||||
"count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"original": bson.M{
|
||||
"$push": "$dates",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"dates": bson.M{
|
||||
"$arrayElemAt": bson.A{"$original", 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"original": 0,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$sort": bson.M{
|
||||
"count": sort,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": nil,
|
||||
"user_count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"users": bson.M{
|
||||
"$push": "$$ROOT",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"dates": bson.M{
|
||||
"$arrayElemAt": bson.A{"$users", 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"dates": "$dates.dates",
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"_id": 0,
|
||||
"users.dates": 0,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"msg_count": bson.M{
|
||||
"$sum": "$users.count",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"users": bson.M{
|
||||
"$slice": bson.A{"$users", pageNumber - 1, showNumber},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cur, err := m.MsgCollection.Aggregate(ctx, pipeline, options.Aggregate().SetAllowDiskUse(true))
|
||||
if err != nil {
|
||||
return 0, 0, nil, nil, errs.Wrap(err)
|
||||
}
|
||||
defer cur.Close(ctx)
|
||||
var result []Result
|
||||
if err := cur.All(ctx, &result); err != nil {
|
||||
return 0, 0, nil, nil, errs.Wrap(err)
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return 0, 0, nil, nil, errs.Wrap(err)
|
||||
}
|
||||
users = make([]*table.UserCount, len(result[0].Users))
|
||||
for i, r := range result[0].Users {
|
||||
users[i] = &table.UserCount{
|
||||
UserID: r.UserID,
|
||||
Count: r.Count,
|
||||
}
|
||||
}
|
||||
dateCount = make(map[string]int64)
|
||||
for _, r := range result[0].Dates {
|
||||
dateCount[r.Date] = r.Count
|
||||
}
|
||||
return result[0].MsgCount, result[0].UserCount, users, dateCount, nil
|
||||
}
|
||||
|
||||
func (m *MsgMongoDriver) RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*table.GroupCount, dateCount map[string]int64, err error) {
|
||||
var sort int
|
||||
if ase {
|
||||
sort = 1
|
||||
} else {
|
||||
sort = -1
|
||||
}
|
||||
type Result struct {
|
||||
MsgCount int64 `bson:"msg_count"`
|
||||
UserCount int64 `bson:"user_count"`
|
||||
Groups []struct {
|
||||
GroupID string `bson:"_id"`
|
||||
Count int64 `bson:"count"`
|
||||
} `bson:"groups"`
|
||||
Dates []struct {
|
||||
Date string `bson:"_id"`
|
||||
Count int64 `bson:"count"`
|
||||
} `bson:"dates"`
|
||||
}
|
||||
pipeline := bson.A{
|
||||
bson.M{
|
||||
"$match": bson.M{
|
||||
"$and": bson.A{
|
||||
bson.M{
|
||||
"msgs.msg.send_time": bson.M{
|
||||
"$gte": start.UnixMilli(),
|
||||
"$lt": end.UnixMilli(),
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$or": bson.A{
|
||||
bson.M{
|
||||
"doc_id": bson.M{
|
||||
"$regex": "^g_",
|
||||
"$options": "i",
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"doc_id": bson.M{
|
||||
"$regex": "^sg_",
|
||||
"$options": "i",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"msgs": bson.M{
|
||||
"$filter": bson.M{
|
||||
"input": "$msgs",
|
||||
"as": "item",
|
||||
"cond": bson.M{
|
||||
"$and": bson.A{
|
||||
bson.M{
|
||||
"$gte": bson.A{
|
||||
"$$item.msg.send_time", start.UnixMilli(),
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$lt": bson.A{
|
||||
"$$item.msg.send_time", end.UnixMilli(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"_id": 0,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"result": bson.M{
|
||||
"$map": bson.M{
|
||||
"input": "$msgs",
|
||||
"as": "item",
|
||||
"in": bson.M{
|
||||
"group_id": "$$item.msg.group_id",
|
||||
"send_date": bson.M{
|
||||
"$dateToString": bson.M{
|
||||
"format": "%Y-%m-%d",
|
||||
"date": bson.M{
|
||||
"$toDate": "$$item.msg.send_time", // 毫秒时间戳
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$unwind": "$result",
|
||||
},
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": "$result.send_date",
|
||||
"count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"original": bson.M{
|
||||
"$push": "$$ROOT",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"dates": "$$ROOT",
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"_id": 0,
|
||||
"count": 0,
|
||||
"dates.original": 0,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": nil,
|
||||
"count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"dates": bson.M{
|
||||
"$push": "$dates",
|
||||
},
|
||||
"original": bson.M{
|
||||
"$push": "$original",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$unwind": "$original",
|
||||
},
|
||||
bson.M{
|
||||
"$unwind": "$original",
|
||||
},
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": "$original.result.group_id",
|
||||
"count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"original": bson.M{
|
||||
"$push": "$dates",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"dates": bson.M{
|
||||
"$arrayElemAt": bson.A{"$original", 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"original": 0,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$sort": bson.M{
|
||||
"count": sort,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": nil,
|
||||
"user_count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"groups": bson.M{
|
||||
"$push": "$$ROOT",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"dates": bson.M{
|
||||
"$arrayElemAt": bson.A{"$groups", 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"dates": "$dates.dates",
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"_id": 0,
|
||||
"groups.dates": 0,
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"msg_count": bson.M{
|
||||
"$sum": "$groups.count",
|
||||
},
|
||||
},
|
||||
},
|
||||
bson.M{
|
||||
"$addFields": bson.M{
|
||||
"groups": bson.M{
|
||||
"$slice": bson.A{"$groups", pageNumber - 1, showNumber},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cur, err := m.MsgCollection.Aggregate(ctx, pipeline, options.Aggregate().SetAllowDiskUse(true))
|
||||
if err != nil {
|
||||
return 0, 0, nil, nil, errs.Wrap(err)
|
||||
}
|
||||
defer cur.Close(ctx)
|
||||
var result []Result
|
||||
if err := cur.All(ctx, &result); err != nil {
|
||||
return 0, 0, nil, nil, errs.Wrap(err)
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return 0, 0, nil, nil, errs.Wrap(err)
|
||||
}
|
||||
groups = make([]*table.GroupCount, len(result[0].Groups))
|
||||
for i, r := range result[0].Groups {
|
||||
groups[i] = &table.GroupCount{
|
||||
GroupID: r.GroupID,
|
||||
Count: r.Count,
|
||||
}
|
||||
}
|
||||
dateCount = make(map[string]int64)
|
||||
for _, r := range result[0].Dates {
|
||||
dateCount[r.Date] = r.Count
|
||||
}
|
||||
return result[0].MsgCount, result[0].UserCount, groups, dateCount, nil
|
||||
}
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
// Copyright © 2023 OpenIM. 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 unrelation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
func NewSuperGroupMongoDriver(database *mongo.Database) unrelation.SuperGroupModelInterface {
|
||||
return &SuperGroupMongoDriver{superGroupCollection: database.Collection(unrelation.CSuperGroup), userToSuperGroupCollection: database.Collection(unrelation.CUserToSuperGroup)}
|
||||
return &SuperGroupMongoDriver{
|
||||
superGroupCollection: database.Collection(unrelation.CSuperGroup),
|
||||
userToSuperGroupCollection: database.Collection(unrelation.CUserToSuperGroup),
|
||||
}
|
||||
}
|
||||
|
||||
type SuperGroupMongoDriver struct {
|
||||
@@ -27,9 +46,14 @@ func (s *SuperGroupMongoDriver) CreateSuperGroup(ctx context.Context, groupID st
|
||||
return err
|
||||
}
|
||||
for _, userID := range initMemberIDs {
|
||||
_, err = s.userToSuperGroupCollection.UpdateOne(ctx, bson.M{"user_id": userID}, bson.M{"$addToSet": bson.M{"group_id_list": groupID}}, &options.UpdateOptions{
|
||||
Upsert: utils.ToPtr(true),
|
||||
})
|
||||
_, err = s.userToSuperGroupCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": userID},
|
||||
bson.M{"$addToSet": bson.M{"group_id_list": groupID}},
|
||||
&options.UpdateOptions{
|
||||
Upsert: utils.ToPtr(true),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -37,14 +61,20 @@ func (s *SuperGroupMongoDriver) CreateSuperGroup(ctx context.Context, groupID st
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SuperGroupMongoDriver) TakeSuperGroup(ctx context.Context, groupID string) (group *unrelation.SuperGroupModel, err error) {
|
||||
func (s *SuperGroupMongoDriver) TakeSuperGroup(
|
||||
ctx context.Context,
|
||||
groupID string,
|
||||
) (group *unrelation.SuperGroupModel, err error) {
|
||||
if err := s.superGroupCollection.FindOne(ctx, bson.M{"group_id": groupID}).Decode(&group); err != nil {
|
||||
return nil, utils.Wrap(err, "")
|
||||
}
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func (s *SuperGroupMongoDriver) FindSuperGroup(ctx context.Context, groupIDs []string) (groups []*unrelation.SuperGroupModel, err error) {
|
||||
func (s *SuperGroupMongoDriver) FindSuperGroup(
|
||||
ctx context.Context,
|
||||
groupIDs []string,
|
||||
) (groups []*unrelation.SuperGroupModel, err error) {
|
||||
cursor, err := s.superGroupCollection.Find(ctx, bson.M{"group_id": bson.M{
|
||||
"$in": groupIDs,
|
||||
}})
|
||||
@@ -59,7 +89,11 @@ func (s *SuperGroupMongoDriver) FindSuperGroup(ctx context.Context, groupIDs []s
|
||||
}
|
||||
|
||||
func (s *SuperGroupMongoDriver) AddUserToSuperGroup(ctx context.Context, groupID string, userIDs []string) error {
|
||||
_, err := s.superGroupCollection.UpdateOne(ctx, bson.M{"group_id": groupID}, bson.M{"$addToSet": bson.M{"member_id_list": bson.M{"$each": userIDs}}})
|
||||
_, err := s.superGroupCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"group_id": groupID},
|
||||
bson.M{"$addToSet": bson.M{"member_id_list": bson.M{"$each": userIDs}}},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -68,7 +102,12 @@ func (s *SuperGroupMongoDriver) AddUserToSuperGroup(ctx context.Context, groupID
|
||||
Upsert: &upsert,
|
||||
}
|
||||
for _, userID := range userIDs {
|
||||
_, err = s.userToSuperGroupCollection.UpdateOne(ctx, bson.M{"user_id": userID}, bson.M{"$addToSet": bson.M{"group_id_list": groupID}}, opts)
|
||||
_, err = s.userToSuperGroupCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": userID},
|
||||
bson.M{"$addToSet": bson.M{"group_id_list": groupID}},
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
return utils.Wrap(err, "transaction failed")
|
||||
}
|
||||
@@ -77,7 +116,11 @@ func (s *SuperGroupMongoDriver) AddUserToSuperGroup(ctx context.Context, groupID
|
||||
}
|
||||
|
||||
func (s *SuperGroupMongoDriver) RemoverUserFromSuperGroup(ctx context.Context, groupID string, userIDs []string) error {
|
||||
_, err := s.superGroupCollection.UpdateOne(ctx, bson.M{"group_id": groupID}, bson.M{"$pull": bson.M{"member_id_list": bson.M{"$in": userIDs}}})
|
||||
_, err := s.superGroupCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"group_id": groupID},
|
||||
bson.M{"$pull": bson.M{"member_id_list": bson.M{"$in": userIDs}}},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -88,7 +131,10 @@ func (s *SuperGroupMongoDriver) RemoverUserFromSuperGroup(ctx context.Context, g
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SuperGroupMongoDriver) GetSuperGroupByUserID(ctx context.Context, userID string) (*unrelation.UserToSuperGroupModel, error) {
|
||||
func (s *SuperGroupMongoDriver) GetSuperGroupByUserID(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
) (*unrelation.UserToSuperGroupModel, error) {
|
||||
var user unrelation.UserToSuperGroupModel
|
||||
err := s.userToSuperGroupCollection.FindOne(ctx, bson.M{"user_id": userID}).Decode(&user)
|
||||
return &user, utils.Wrap(err, "")
|
||||
@@ -106,6 +152,10 @@ func (s *SuperGroupMongoDriver) DeleteSuperGroup(ctx context.Context, groupID st
|
||||
}
|
||||
|
||||
func (s *SuperGroupMongoDriver) RemoveGroupFromUser(ctx context.Context, groupID string, userIDs []string) error {
|
||||
_, err := s.userToSuperGroupCollection.UpdateOne(ctx, bson.M{"user_id": bson.M{"$in": userIDs}}, bson.M{"$pull": bson.M{"group_id_list": groupID}})
|
||||
_, err := s.userToSuperGroupCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": bson.M{"$in": userIDs}},
|
||||
bson.M{"$pull": bson.M{"group_id_list": groupID}},
|
||||
)
|
||||
return utils.Wrap(err, "")
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
** copyright('open-im,www.open-im.io').
|
||||
** author("fg,Gordon@tuoyun.net").
|
||||
** time(2021/5/27 10:31).
|
||||
*/
|
||||
package http
|
||||
*/package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -39,7 +38,13 @@ func Get(url string) (response []byte, err error) {
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func Post(ctx context.Context, url string, header map[string]string, data interface{}, timeout int) (content []byte, err error) {
|
||||
func Post(
|
||||
ctx context.Context,
|
||||
url string,
|
||||
header map[string]string,
|
||||
data interface{},
|
||||
timeout int,
|
||||
) (content []byte, err error) {
|
||||
if timeout > 0 {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(timeout))
|
||||
@@ -72,7 +77,13 @@ func Post(ctx context.Context, url string, header map[string]string, data interf
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func PostReturn(ctx context.Context, url string, header map[string]string, input, output interface{}, timeOutSecond int) error {
|
||||
func PostReturn(
|
||||
ctx context.Context,
|
||||
url string,
|
||||
header map[string]string,
|
||||
input, output interface{},
|
||||
timeOutSecond int,
|
||||
) error {
|
||||
b, err := Post(ctx, url, header, input, timeOutSecond)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -81,7 +92,13 @@ func PostReturn(ctx context.Context, url string, header map[string]string, input
|
||||
return err
|
||||
}
|
||||
|
||||
func callBackPostReturn(ctx context.Context, url, command string, input interface{}, output callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error {
|
||||
func callBackPostReturn(
|
||||
ctx context.Context,
|
||||
url, command string,
|
||||
input interface{},
|
||||
output callbackstruct.CallbackResp,
|
||||
callbackConfig config.CallBackConfig,
|
||||
) error {
|
||||
defer log.ZDebug(ctx, "callback", "url", url, "command", command, "input", input, "callbackConfig", callbackConfig)
|
||||
v := urlLib.Values{}
|
||||
v.Set(constant.CallbackCommand, command)
|
||||
@@ -104,6 +121,12 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac
|
||||
return output.Parse()
|
||||
}
|
||||
|
||||
func CallBackPostReturn(ctx context.Context, url string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error {
|
||||
func CallBackPostReturn(
|
||||
ctx context.Context,
|
||||
url string,
|
||||
req callbackstruct.CallbackReq,
|
||||
resp callbackstruct.CallbackResp,
|
||||
callbackConfig config.CallBackConfig,
|
||||
) error {
|
||||
return callBackPostReturn(ctx, url, req.GetCallbackCommand(), req, resp, callbackConfig)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright © 2023 OpenIM. 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 kafka
|
||||
|
||||
import (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user