复制项目
This commit is contained in:
38
pkg/common/apistruct/admin.go
Normal file
38
pkg/common/apistruct/admin.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 apistruct
|
||||
|
||||
import "git.imall.cloud/openim/protocol/sdkws"
|
||||
|
||||
type AdminLoginResp struct {
|
||||
AdminAccount string `json:"adminAccount"`
|
||||
AdminToken string `json:"adminToken"`
|
||||
Nickname string `json:"nickname"`
|
||||
FaceURL string `json:"faceURL"`
|
||||
Level int32 `json:"level"`
|
||||
AdminUserID string `json:"adminUserID"`
|
||||
ImUserID string `json:"imUserID"`
|
||||
ImToken string `json:"imToken"`
|
||||
}
|
||||
|
||||
type SearchDefaultGroupResp struct {
|
||||
Total uint32 `json:"total"`
|
||||
Groups []*sdkws.GroupInfo `json:"groups"`
|
||||
}
|
||||
|
||||
type NewUserCountResp struct {
|
||||
Total int64 `json:"total"`
|
||||
DateCount map[string]int64 `json:"date_count"`
|
||||
}
|
||||
133
pkg/common/apistruct/chat.go
Normal file
133
pkg/common/apistruct/chat.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 apistruct
|
||||
|
||||
import "git.imall.cloud/openim/protocol/sdkws"
|
||||
|
||||
type UserRegisterResp struct {
|
||||
ImToken string `json:"imToken"`
|
||||
ChatToken string `json:"chatToken"`
|
||||
UserID string `json:"userID"`
|
||||
}
|
||||
|
||||
type LoginResp struct {
|
||||
ImToken string `json:"imToken"`
|
||||
ChatToken string `json:"chatToken"`
|
||||
UserID string `json:"userID"`
|
||||
}
|
||||
|
||||
type UpdateUserInfoResp struct{}
|
||||
|
||||
type CallbackAfterSendSingleMsgReq struct {
|
||||
CommonCallbackReq
|
||||
RecvID string `json:"recvID"`
|
||||
}
|
||||
|
||||
type CommonCallbackReq struct {
|
||||
SendID string `json:"sendID"`
|
||||
CallbackCommand string `json:"callbackCommand"`
|
||||
ServerMsgID string `json:"serverMsgID"`
|
||||
ClientMsgID string `json:"clientMsgID"`
|
||||
OperationID string `json:"operationID"`
|
||||
SenderPlatformID int32 `json:"senderPlatformID"`
|
||||
SenderNickname string `json:"senderNickname"`
|
||||
SessionType int32 `json:"sessionType"`
|
||||
MsgFrom int32 `json:"msgFrom"`
|
||||
ContentType int32 `json:"contentType"`
|
||||
Status int32 `json:"status"`
|
||||
CreateTime int64 `json:"createTime"`
|
||||
Content string `json:"content"`
|
||||
Seq uint32 `json:"seq"`
|
||||
AtUserIDList []string `json:"atUserList"`
|
||||
SenderFaceURL string `json:"faceURL"`
|
||||
Ex string `json:"ex"`
|
||||
}
|
||||
|
||||
type CallbackAfterSendSingleMsgResp struct {
|
||||
CommonCallbackResp
|
||||
}
|
||||
|
||||
type CommonCallbackResp struct {
|
||||
ActionCode int32 `json:"actionCode"`
|
||||
ErrCode int32 `json:"errCode"`
|
||||
ErrMsg string `json:"errMsg"`
|
||||
ErrDlt string `json:"errDlt"`
|
||||
NextCode int32 `json:"nextCode"`
|
||||
}
|
||||
|
||||
type TextElem struct {
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
type PictureElem struct {
|
||||
SourcePath string `mapstructure:"sourcePath"`
|
||||
SourcePicture PictureBaseInfo `mapstructure:"sourcePicture" validate:"required"`
|
||||
BigPicture PictureBaseInfo `mapstructure:"bigPicture" validate:"required"`
|
||||
SnapshotPicture PictureBaseInfo `mapstructure:"snapshotPicture" validate:"required"`
|
||||
}
|
||||
|
||||
type PictureBaseInfo struct {
|
||||
UUID string `mapstructure:"uuid"`
|
||||
Type string `mapstructure:"type" validate:"required"`
|
||||
Size int64 `mapstructure:"size"`
|
||||
Width int32 `mapstructure:"width" validate:"required"`
|
||||
Height int32 `mapstructure:"height" validate:"required"`
|
||||
Url string `mapstructure:"url" validate:"required"`
|
||||
}
|
||||
|
||||
type SendMsgReq struct {
|
||||
// RecvID uniquely identifies the receiver and is required for one-on-one or notification chat types.
|
||||
RecvID string `json:"recvID" binding:"required_if" message:"recvID is required if sessionType is SingleChatType or NotificationChatType"`
|
||||
SendMsg
|
||||
}
|
||||
|
||||
// SendMsg defines the structure for sending messages with various metadata.
|
||||
type SendMsg struct {
|
||||
// SendID uniquely identifies the sender.
|
||||
SendID string `json:"sendID" binding:"required"`
|
||||
|
||||
// GroupID is the identifier for the group, required if SessionType is 2 or 3.
|
||||
GroupID string `json:"groupID" binding:"required_if=SessionType 2|required_if=SessionType 3"`
|
||||
|
||||
// SenderNickname is the nickname of the sender.
|
||||
SenderNickname string `json:"senderNickname"`
|
||||
|
||||
// SenderFaceURL is the URL to the sender's avatar.
|
||||
SenderFaceURL string `json:"senderFaceURL"`
|
||||
|
||||
// SenderPlatformID is an integer identifier for the sender's platform.
|
||||
SenderPlatformID int32 `json:"senderPlatformID"`
|
||||
|
||||
// Content is the actual content of the message, required and excluded from Swagger documentation.
|
||||
Content map[string]any `json:"content" binding:"required" swaggerignore:"true"`
|
||||
|
||||
// ContentType is an integer that represents the type of the content.
|
||||
ContentType int32 `json:"contentType" binding:"required"`
|
||||
|
||||
// SessionType is an integer that represents the type of session for the message.
|
||||
SessionType int32 `json:"sessionType" binding:"required"`
|
||||
|
||||
// IsOnlineOnly specifies if the message is only sent when the receiver is online.
|
||||
IsOnlineOnly bool `json:"isOnlineOnly"`
|
||||
|
||||
// NotOfflinePush specifies if the message should not trigger offline push notifications.
|
||||
NotOfflinePush bool `json:"notOfflinePush"`
|
||||
|
||||
// SendTime is a timestamp indicating when the message was sent.
|
||||
SendTime int64 `json:"sendTime"`
|
||||
|
||||
// OfflinePushInfo contains information for offline push notifications.
|
||||
OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"`
|
||||
}
|
||||
28
pkg/common/apistruct/config_manager.go
Normal file
28
pkg/common/apistruct/config_manager.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package apistruct
|
||||
|
||||
type GetConfigReq struct {
|
||||
ConfigName string `json:"configName"`
|
||||
}
|
||||
|
||||
type GetConfigListResp struct {
|
||||
Environment string `json:"environment"`
|
||||
Version string `json:"version"`
|
||||
ConfigNames []string `json:"configNames"`
|
||||
}
|
||||
|
||||
type SetConfigReq struct {
|
||||
ConfigName string `json:"configName"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
type SetConfigsReq struct {
|
||||
Configs []SetConfigReq `json:"configs"`
|
||||
}
|
||||
|
||||
type SetEnableConfigManagerReq struct {
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type GetEnableConfigManagerResp struct {
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
49
pkg/common/cmd/admin_api.go
Normal file
49
pkg/common/cmd/admin_api.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/internal/api/admin"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type AdminApiCmd struct {
|
||||
*RootCmd
|
||||
ctx context.Context
|
||||
configMap map[string]any
|
||||
apiConfig admin.Config
|
||||
}
|
||||
|
||||
func NewAdminApiCmd() *AdminApiCmd {
|
||||
ret := AdminApiCmd{apiConfig: admin.Config{
|
||||
AllConfig: &config.AllConfig{},
|
||||
}}
|
||||
ret.configMap = map[string]any{
|
||||
config.DiscoveryConfigFileName: &ret.apiConfig.Discovery,
|
||||
config.LogConfigFileName: &ret.apiConfig.Log,
|
||||
config.MongodbConfigFileName: &ret.apiConfig.Mongo,
|
||||
config.ChatAPIAdminCfgFileName: &ret.apiConfig.AdminAPI,
|
||||
config.ChatAPIChatCfgFileName: &ret.apiConfig.ChatAPI,
|
||||
config.ChatRPCAdminCfgFileName: &ret.apiConfig.Admin,
|
||||
config.ChatRPCChatCfgFileName: &ret.apiConfig.Chat,
|
||||
config.RedisConfigFileName: &ret.apiConfig.Redis,
|
||||
config.ShareFileName: &ret.apiConfig.Share,
|
||||
}
|
||||
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
|
||||
ret.ctx = context.WithValue(context.Background(), "version", config.Version)
|
||||
ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ret.apiConfig.ConfigPath = ret.configPath
|
||||
return ret.runE()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (a *AdminApiCmd) Exec() error {
|
||||
return a.Execute()
|
||||
}
|
||||
|
||||
func (a *AdminApiCmd) runE() error {
|
||||
return admin.Start(a.ctx, a.Index(), &a.apiConfig)
|
||||
}
|
||||
68
pkg/common/cmd/admin_rpc.go
Normal file
68
pkg/common/cmd/admin_rpc.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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 (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/internal/rpc/admin"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"git.imall.cloud/openim/chat/pkg/common/startrpc"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type AdminRpcCmd struct {
|
||||
*RootCmd
|
||||
ctx context.Context
|
||||
configMap map[string]any
|
||||
adminConfig admin.Config
|
||||
}
|
||||
|
||||
func NewAdminRpcCmd() *AdminRpcCmd {
|
||||
var ret AdminRpcCmd
|
||||
ret.configMap = map[string]any{
|
||||
config.ChatRPCAdminCfgFileName: &ret.adminConfig.RpcConfig,
|
||||
config.RedisConfigFileName: &ret.adminConfig.RedisConfig,
|
||||
config.DiscoveryConfigFileName: &ret.adminConfig.Discovery,
|
||||
config.MongodbConfigFileName: &ret.adminConfig.MongodbConfig,
|
||||
config.ShareFileName: &ret.adminConfig.Share,
|
||||
}
|
||||
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
|
||||
ret.ctx = context.WithValue(context.Background(), "version", config.Version)
|
||||
ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
return ret.runE()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (a *AdminRpcCmd) Exec() error {
|
||||
return a.Execute()
|
||||
}
|
||||
|
||||
func (a *AdminRpcCmd) runE() error {
|
||||
return startrpc.Start(a.ctx, &a.adminConfig.Discovery, a.adminConfig.RpcConfig.RPC.ListenIP,
|
||||
a.adminConfig.RpcConfig.RPC.RegisterIP, a.adminConfig.RpcConfig.RPC.Ports,
|
||||
a.Index(), a.adminConfig.Discovery.RpcService.Admin, &a.adminConfig.Share, &a.adminConfig,
|
||||
[]string{
|
||||
config.ChatRPCAdminCfgFileName,
|
||||
config.RedisConfigFileName,
|
||||
config.DiscoveryConfigFileName,
|
||||
config.MongodbConfigFileName,
|
||||
config.ShareFileName,
|
||||
config.LogConfigFileName,
|
||||
}, nil,
|
||||
admin.Start)
|
||||
}
|
||||
41
pkg/common/cmd/bot_api.go
Normal file
41
pkg/common/cmd/bot_api.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/internal/api/bot"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type BotApiCmd struct {
|
||||
*RootCmd
|
||||
ctx context.Context
|
||||
configMap map[string]any
|
||||
apiConfig bot.Config
|
||||
}
|
||||
|
||||
func NewBotApiCmd() *BotApiCmd {
|
||||
ret := BotApiCmd{apiConfig: bot.Config{}}
|
||||
ret.configMap = map[string]any{
|
||||
config.DiscoveryConfigFileName: &ret.apiConfig.Discovery,
|
||||
config.ChatAPIBotCfgFileName: &ret.apiConfig.ApiConfig,
|
||||
config.ShareFileName: &ret.apiConfig.Share,
|
||||
config.RedisConfigFileName: &ret.apiConfig.Redis,
|
||||
}
|
||||
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
|
||||
ret.ctx = context.WithValue(context.Background(), "version", config.Version)
|
||||
ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
return ret.runE()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (a *BotApiCmd) Exec() error {
|
||||
return a.Execute()
|
||||
}
|
||||
|
||||
func (a *BotApiCmd) runE() error {
|
||||
return bot.Start(a.ctx, a.Index(), &a.apiConfig)
|
||||
}
|
||||
54
pkg/common/cmd/bot_rpc.go
Normal file
54
pkg/common/cmd/bot_rpc.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/internal/rpc/bot"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"git.imall.cloud/openim/chat/pkg/common/startrpc"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type BotRpcCmd struct {
|
||||
*RootCmd
|
||||
ctx context.Context
|
||||
configMap map[string]any
|
||||
botConfig bot.Config
|
||||
}
|
||||
|
||||
func NewBotRpcCmd() *BotRpcCmd {
|
||||
var ret BotRpcCmd
|
||||
ret.configMap = map[string]any{
|
||||
config.ChatRPCBotCfgFileName: &ret.botConfig.RpcConfig,
|
||||
config.RedisConfigFileName: &ret.botConfig.RedisConfig,
|
||||
config.DiscoveryConfigFileName: &ret.botConfig.Discovery,
|
||||
config.MongodbConfigFileName: &ret.botConfig.MongodbConfig,
|
||||
config.ShareFileName: &ret.botConfig.Share,
|
||||
}
|
||||
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
|
||||
ret.ctx = context.WithValue(context.Background(), "version", config.Version)
|
||||
ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
return ret.runE()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (a *BotRpcCmd) Exec() error {
|
||||
return a.Execute()
|
||||
}
|
||||
|
||||
func (a *BotRpcCmd) runE() error {
|
||||
return startrpc.Start(a.ctx, &a.botConfig.Discovery, a.botConfig.RpcConfig.RPC.ListenIP,
|
||||
a.botConfig.RpcConfig.RPC.RegisterIP, a.botConfig.RpcConfig.RPC.Ports,
|
||||
a.Index(), a.botConfig.Discovery.RpcService.Bot, &a.botConfig.Share, &a.botConfig,
|
||||
[]string{
|
||||
config.ChatRPCBotCfgFileName,
|
||||
config.RedisConfigFileName,
|
||||
config.DiscoveryConfigFileName,
|
||||
config.MongodbConfigFileName,
|
||||
config.ShareFileName,
|
||||
config.LogConfigFileName,
|
||||
}, nil,
|
||||
bot.Start)
|
||||
}
|
||||
41
pkg/common/cmd/chat_api.go
Normal file
41
pkg/common/cmd/chat_api.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/internal/api/chat"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type ChatApiCmd struct {
|
||||
*RootCmd
|
||||
ctx context.Context
|
||||
configMap map[string]any
|
||||
apiConfig chat.Config
|
||||
}
|
||||
|
||||
func NewChatApiCmd() *ChatApiCmd {
|
||||
var ret ChatApiCmd
|
||||
ret.configMap = map[string]any{
|
||||
config.ShareFileName: &ret.apiConfig.Share,
|
||||
config.ChatAPIChatCfgFileName: &ret.apiConfig.ApiConfig,
|
||||
config.DiscoveryConfigFileName: &ret.apiConfig.Discovery,
|
||||
config.RedisConfigFileName: &ret.apiConfig.Redis,
|
||||
}
|
||||
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
|
||||
ret.ctx = context.WithValue(context.Background(), "version", config.Version)
|
||||
ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
return ret.runE()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (a *ChatApiCmd) Exec() error {
|
||||
return a.Execute()
|
||||
}
|
||||
|
||||
func (a *ChatApiCmd) runE() error {
|
||||
return chat.Start(a.ctx, a.Index(), &a.apiConfig)
|
||||
}
|
||||
68
pkg/common/cmd/chat_rpc.go
Normal file
68
pkg/common/cmd/chat_rpc.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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 (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/internal/rpc/chat"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"git.imall.cloud/openim/chat/pkg/common/startrpc"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type ChatRpcCmd struct {
|
||||
*RootCmd
|
||||
ctx context.Context
|
||||
configMap map[string]any
|
||||
chatConfig chat.Config
|
||||
}
|
||||
|
||||
func NewChatRpcCmd() *ChatRpcCmd {
|
||||
var ret ChatRpcCmd
|
||||
ret.configMap = map[string]any{
|
||||
config.ChatRPCChatCfgFileName: &ret.chatConfig.RpcConfig,
|
||||
config.RedisConfigFileName: &ret.chatConfig.RedisConfig,
|
||||
config.DiscoveryConfigFileName: &ret.chatConfig.Discovery,
|
||||
config.MongodbConfigFileName: &ret.chatConfig.MongodbConfig,
|
||||
config.ShareFileName: &ret.chatConfig.Share,
|
||||
}
|
||||
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
|
||||
ret.ctx = context.WithValue(context.Background(), "version", config.Version)
|
||||
ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
return ret.runE()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (a *ChatRpcCmd) Exec() error {
|
||||
return a.Execute()
|
||||
}
|
||||
|
||||
func (a *ChatRpcCmd) runE() error {
|
||||
return startrpc.Start(a.ctx, &a.chatConfig.Discovery, a.chatConfig.RpcConfig.RPC.ListenIP,
|
||||
a.chatConfig.RpcConfig.RPC.RegisterIP, a.chatConfig.RpcConfig.RPC.Ports,
|
||||
a.Index(), a.chatConfig.Discovery.RpcService.Chat, &a.chatConfig.Share, &a.chatConfig,
|
||||
[]string{
|
||||
config.ChatRPCChatCfgFileName,
|
||||
config.RedisConfigFileName,
|
||||
config.DiscoveryConfigFileName,
|
||||
config.MongodbConfigFileName,
|
||||
config.ShareFileName,
|
||||
config.LogConfigFileName,
|
||||
}, nil,
|
||||
chat.Start)
|
||||
}
|
||||
266
pkg/common/cmd/root.go
Normal file
266
pkg/common/cmd/root.go
Normal file
@@ -0,0 +1,266 @@
|
||||
// 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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"git.imall.cloud/openim/chat/pkg/common/kdisc"
|
||||
disetcd "git.imall.cloud/openim/chat/pkg/common/kdisc/etcd"
|
||||
"git.imall.cloud/openim/chat/version"
|
||||
"github.com/openimsdk/tools/discovery/etcd"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/runtimeenv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type RootCmd struct {
|
||||
Command cobra.Command
|
||||
processName string
|
||||
port int
|
||||
prometheusPort int
|
||||
log config.Log
|
||||
index int
|
||||
configPath string
|
||||
etcdClient *clientv3.Client
|
||||
}
|
||||
|
||||
func (r *RootCmd) Index() int {
|
||||
return r.index
|
||||
}
|
||||
|
||||
func (r *RootCmd) Port() int {
|
||||
return r.port
|
||||
}
|
||||
|
||||
type CmdOpts struct {
|
||||
loggerPrefixName string
|
||||
configMap map[string]any
|
||||
}
|
||||
|
||||
func WithLogName(logName string) func(*CmdOpts) {
|
||||
return func(opts *CmdOpts) {
|
||||
opts.loggerPrefixName = logName
|
||||
}
|
||||
}
|
||||
func WithConfigMap(configMap map[string]any) func(*CmdOpts) {
|
||||
return func(opts *CmdOpts) {
|
||||
opts.configMap = configMap
|
||||
}
|
||||
}
|
||||
|
||||
func NewRootCmd(processName string, opts ...func(*CmdOpts)) *RootCmd {
|
||||
rootCmd := &RootCmd{processName: processName}
|
||||
cmd := cobra.Command{
|
||||
Use: "Start openIM chat application",
|
||||
Long: fmt.Sprintf(`Start %s `, processName),
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return rootCmd.persistentPreRun(cmd, opts...)
|
||||
},
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: false,
|
||||
}
|
||||
cmd.Flags().StringP(config.FlagConf, "c", "", "path of config directory")
|
||||
cmd.Flags().IntP(config.FlagTransferIndex, "i", 0, "process startup sequence number")
|
||||
|
||||
rootCmd.Command = cmd
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
func (r *RootCmd) initEtcd() error {
|
||||
configDirectory, _, err := r.getFlag(&r.Command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
disConfig := config.Discovery{}
|
||||
env := runtimeenv.PrintRuntimeEnvironment()
|
||||
err = config.Load(configDirectory, config.DiscoveryConfigFileName, config.EnvPrefixMap[config.DiscoveryConfigFileName],
|
||||
env, &disConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if disConfig.Enable == kdisc.ETCDCONST {
|
||||
discov, _ := kdisc.NewDiscoveryRegister(&disConfig, env, nil)
|
||||
// 安全类型断言:仅当为 etcd 实现时才获取客户端,避免在 K8s 模式下崩溃
|
||||
if etcdDiscov, ok := discov.(*etcd.SvcDiscoveryRegistryImpl); ok {
|
||||
r.etcdClient = etcdDiscov.GetClient()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RootCmd) persistentPreRun(cmd *cobra.Command, opts ...func(*CmdOpts)) error {
|
||||
if err := r.initEtcd(); err != nil {
|
||||
return err
|
||||
}
|
||||
cmdOpts := r.applyOptions(opts...)
|
||||
if err := r.initializeConfiguration(cmd, cmdOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.updateConfigFromEtcd(cmdOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.initializeLogger(cmdOpts); err != nil {
|
||||
return errs.WrapMsg(err, "failed to initialize logger")
|
||||
}
|
||||
if r.etcdClient != nil {
|
||||
if err := r.etcdClient.Close(); err != nil {
|
||||
return errs.WrapMsg(err, "failed to close etcd client")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RootCmd) initializeConfiguration(cmd *cobra.Command, opts *CmdOpts) error {
|
||||
configDirectory, _, err := r.getFlag(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.configPath = configDirectory
|
||||
|
||||
runtimeEnv := runtimeenv.PrintRuntimeEnvironment()
|
||||
|
||||
// Load common configuration file
|
||||
//opts.configMap[ShareFileName] = StructEnvPrefix{EnvPrefix: shareEnvPrefix, ConfigStruct: &r.share}
|
||||
for configFileName, configStruct := range opts.configMap {
|
||||
err := config.Load(configDirectory, configFileName,
|
||||
config.EnvPrefixMap[configFileName], runtimeEnv, configStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Load common log configuration file
|
||||
return config.Load(configDirectory, config.LogConfigFileName,
|
||||
config.EnvPrefixMap[config.LogConfigFileName], runtimeEnv, &r.log)
|
||||
}
|
||||
|
||||
func (r *RootCmd) updateConfigFromEtcd(opts *CmdOpts) error {
|
||||
if r.etcdClient == nil {
|
||||
return nil
|
||||
}
|
||||
ctx := context.TODO()
|
||||
|
||||
res, err := r.etcdClient.Get(ctx, disetcd.BuildKey(disetcd.EnableConfigCenterKey))
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "root cmd updateConfigFromEtcd, etcd Get EnableConfigCenterKey err: %v", errs.Wrap(err))
|
||||
return nil
|
||||
}
|
||||
if res.Count == 0 {
|
||||
return nil
|
||||
} else {
|
||||
if string(res.Kvs[0].Value) == disetcd.Disable {
|
||||
return nil
|
||||
} else if string(res.Kvs[0].Value) != disetcd.Enable {
|
||||
return errs.New("unknown EnableConfigCenter value").Wrap()
|
||||
}
|
||||
}
|
||||
|
||||
update := func(configFileName string, configStruct any) error {
|
||||
key := disetcd.BuildKey(configFileName)
|
||||
etcdRes, err := r.etcdClient.Get(ctx, key)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "root cmd updateConfigFromEtcd, etcd Get err: %v", errs.Wrap(err))
|
||||
return nil
|
||||
}
|
||||
if etcdRes.Count == 0 {
|
||||
data, err := json.Marshal(configStruct)
|
||||
if err != nil {
|
||||
return errs.ErrArgs.WithDetail(err.Error()).Wrap()
|
||||
}
|
||||
_, err = r.etcdClient.Put(ctx, disetcd.BuildKey(configFileName), string(data))
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "root cmd updateConfigFromEtcd, etcd Put err: %v", errs.Wrap(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = json.Unmarshal(etcdRes.Kvs[0].Value, configStruct)
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "failed to unmarshal config from etcd")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
for configFileName, configStruct := range opts.configMap {
|
||||
if err := update(configFileName, configStruct); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := update(config.LogConfigFileName, &r.log); err != nil {
|
||||
return err
|
||||
}
|
||||
// Load common log configuration file
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *RootCmd) applyOptions(opts ...func(*CmdOpts)) *CmdOpts {
|
||||
cmdOpts := defaultCmdOpts()
|
||||
for _, opt := range opts {
|
||||
opt(cmdOpts)
|
||||
}
|
||||
|
||||
return cmdOpts
|
||||
}
|
||||
|
||||
func (r *RootCmd) initializeLogger(cmdOpts *CmdOpts) error {
|
||||
err := log.InitLoggerFromConfig(
|
||||
cmdOpts.loggerPrefixName,
|
||||
r.processName,
|
||||
"", "",
|
||||
r.log.RemainLogLevel,
|
||||
r.log.IsStdout,
|
||||
r.log.IsJson,
|
||||
r.log.StorageLocation,
|
||||
r.log.RemainRotationCount,
|
||||
r.log.RotationTime,
|
||||
version.Version,
|
||||
r.log.IsSimplify,
|
||||
)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return errs.Wrap(log.InitConsoleLogger(r.processName, r.log.RemainLogLevel, r.log.IsJson, config.Version))
|
||||
|
||||
}
|
||||
|
||||
func defaultCmdOpts() *CmdOpts {
|
||||
return &CmdOpts{
|
||||
loggerPrefixName: "openim-chat-log",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RootCmd) getFlag(cmd *cobra.Command) (string, int, error) {
|
||||
configDirectory, err := cmd.Flags().GetString(config.FlagConf)
|
||||
if err != nil {
|
||||
return "", 0, errs.Wrap(err)
|
||||
}
|
||||
index, err := cmd.Flags().GetInt(config.FlagTransferIndex)
|
||||
if err != nil {
|
||||
return "", 0, errs.Wrap(err)
|
||||
}
|
||||
r.index = index
|
||||
return configDirectory, index, nil
|
||||
}
|
||||
|
||||
func (r *RootCmd) Execute() error {
|
||||
return r.Command.Execute()
|
||||
}
|
||||
247
pkg/common/config/config.go
Normal file
247
pkg/common/config/config.go
Normal file
@@ -0,0 +1,247 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/redisutil"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed version
|
||||
Version string
|
||||
//go:embed template.xlsx
|
||||
ImportTemplate []byte
|
||||
)
|
||||
|
||||
type Share struct {
|
||||
OpenIM struct {
|
||||
ApiURL string `mapstructure:"apiURL"`
|
||||
Secret string `mapstructure:"secret"`
|
||||
AdminUserID string `mapstructure:"adminUserID"`
|
||||
TokenRefreshInterval int `mapstructure:"tokenRefreshInterval"`
|
||||
} `mapstructure:"openIM"`
|
||||
ChatAdmin []string `mapstructure:"chatAdmin"`
|
||||
ProxyHeader string `mapstructure:"proxyHeader"`
|
||||
}
|
||||
|
||||
type RpcService struct {
|
||||
Chat string `mapstructure:"chat"`
|
||||
Admin string `mapstructure:"admin"`
|
||||
Bot string `mapstructure:"bot"`
|
||||
}
|
||||
|
||||
func (r *RpcService) GetServiceNames() []string {
|
||||
return []string{
|
||||
r.Chat,
|
||||
r.Admin,
|
||||
}
|
||||
}
|
||||
|
||||
type API struct {
|
||||
Api struct {
|
||||
ListenIP string `mapstructure:"listenIP"`
|
||||
Ports []int `mapstructure:"ports"`
|
||||
} `mapstructure:"api"`
|
||||
}
|
||||
|
||||
type APIBot struct {
|
||||
Api struct {
|
||||
ListenIP string `mapstructure:"listenIP"`
|
||||
Ports []int `mapstructure:"ports"`
|
||||
} `mapstructure:"api"`
|
||||
}
|
||||
|
||||
type Mongo struct {
|
||||
URI string `mapstructure:"uri"`
|
||||
Address []string `mapstructure:"address"`
|
||||
Database string `mapstructure:"database"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
AuthSource string `mapstructure:"authSource"`
|
||||
MaxPoolSize int `mapstructure:"maxPoolSize"`
|
||||
MaxRetry int `mapstructure:"maxRetry"`
|
||||
}
|
||||
|
||||
func (m *Mongo) Build() *mongoutil.Config {
|
||||
return &mongoutil.Config{
|
||||
Uri: m.URI,
|
||||
Address: m.Address,
|
||||
Database: m.Database,
|
||||
Username: m.Username,
|
||||
Password: m.Password,
|
||||
AuthSource: m.AuthSource,
|
||||
MaxPoolSize: m.MaxPoolSize,
|
||||
MaxRetry: m.MaxRetry,
|
||||
}
|
||||
}
|
||||
|
||||
type Redis struct {
|
||||
Address []string `mapstructure:"address"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
EnablePipeline bool `mapstructure:"enablePipeline"`
|
||||
ClusterMode bool `mapstructure:"clusterMode"`
|
||||
DB int `mapstructure:"db"`
|
||||
MaxRetry int `mapstructure:"MaxRetry"`
|
||||
}
|
||||
|
||||
func (r *Redis) Build() *redisutil.Config {
|
||||
return &redisutil.Config{
|
||||
ClusterMode: r.ClusterMode,
|
||||
Address: r.Address,
|
||||
Username: r.Username,
|
||||
Password: r.Password,
|
||||
DB: r.DB,
|
||||
MaxRetry: r.MaxRetry,
|
||||
}
|
||||
}
|
||||
|
||||
type Discovery struct {
|
||||
Enable string `mapstructure:"enable"`
|
||||
Etcd Etcd `mapstructure:"etcd"`
|
||||
Kubernetes Kubernetes `mapstructure:"kubernetes"`
|
||||
RpcService RpcService `mapstructure:"rpcService"`
|
||||
}
|
||||
|
||||
type Kubernetes struct {
|
||||
Namespace string `mapstructure:"namespace"`
|
||||
}
|
||||
|
||||
type Etcd struct {
|
||||
RootDirectory string `mapstructure:"rootDirectory"`
|
||||
Address []string `mapstructure:"address"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
}
|
||||
|
||||
type Chat struct {
|
||||
RPC struct {
|
||||
RegisterIP string `mapstructure:"registerIP"`
|
||||
ListenIP string `mapstructure:"listenIP"`
|
||||
Ports []int `mapstructure:"ports"`
|
||||
} `mapstructure:"rpc"`
|
||||
VerifyCode VerifyCode `mapstructure:"verifyCode"`
|
||||
LiveKit struct {
|
||||
URL string `mapstructure:"url"`
|
||||
Key string `mapstructure:"key"`
|
||||
Secret string `mapstructure:"secret"`
|
||||
} `mapstructure:"liveKit"`
|
||||
AllowRegister bool `mapstructure:"allowRegister"`
|
||||
}
|
||||
|
||||
type Bot struct {
|
||||
RPC struct {
|
||||
RegisterIP string `mapstructure:"registerIP"`
|
||||
ListenIP string `mapstructure:"listenIP"`
|
||||
Ports []int `mapstructure:"ports"`
|
||||
} `mapstructure:"rpc"`
|
||||
Timeout int `mapstructure:"timeout"`
|
||||
}
|
||||
type VerifyCode struct {
|
||||
ValidTime int `mapstructure:"validTime"`
|
||||
ValidCount int `mapstructure:"validCount"`
|
||||
UintTime int `mapstructure:"uintTime"`
|
||||
MaxCount int `mapstructure:"maxCount"`
|
||||
SuperCode string `mapstructure:"superCode"`
|
||||
Len int `mapstructure:"len"`
|
||||
Phone struct {
|
||||
Use string `mapstructure:"use"`
|
||||
Ali struct {
|
||||
Endpoint string `mapstructure:"endpoint"`
|
||||
AccessKeyID string `mapstructure:"accessKeyId"`
|
||||
AccessKeySecret string `mapstructure:"accessKeySecret"`
|
||||
SignName string `mapstructure:"signName"`
|
||||
VerificationCodeTemplateCode string `mapstructure:"verificationCodeTemplateCode"`
|
||||
} `mapstructure:"ali"`
|
||||
Bao struct {
|
||||
Endpoint string `mapstructure:"endpoint"`
|
||||
AccessKeyID string `mapstructure:"accessKeyId"`
|
||||
AccessKeySecret string `mapstructure:"accessKeySecret"`
|
||||
SignName string `mapstructure:"signName"`
|
||||
VerificationCodeTemplateCode string `mapstructure:"verificationCodeTemplateCode"`
|
||||
} `mapstructure:"bao"`
|
||||
} `mapstructure:"phone"`
|
||||
Mail struct {
|
||||
Use string `mapstructure:"use"`
|
||||
Title string `mapstructure:"title"`
|
||||
SenderMail string `mapstructure:"senderMail"`
|
||||
SenderAuthorizationCode string `mapstructure:"senderAuthorizationCode"`
|
||||
SMTPAddr string `mapstructure:"smtpAddr"`
|
||||
SMTPPort int `mapstructure:"smtpPort"`
|
||||
} `mapstructure:"mail"`
|
||||
}
|
||||
|
||||
type Admin struct {
|
||||
RPC struct {
|
||||
RegisterIP string `mapstructure:"registerIP"`
|
||||
ListenIP string `mapstructure:"listenIP"`
|
||||
Ports []int `mapstructure:"ports"`
|
||||
} `mapstructure:"rpc"`
|
||||
TokenPolicy struct {
|
||||
Expire int `mapstructure:"expire"`
|
||||
} `mapstructure:"tokenPolicy"`
|
||||
Secret string `mapstructure:"secret"`
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
StorageLocation string `mapstructure:"storageLocation"`
|
||||
RotationTime uint `mapstructure:"rotationTime"`
|
||||
RemainRotationCount uint `mapstructure:"remainRotationCount"`
|
||||
RemainLogLevel int `mapstructure:"remainLogLevel"`
|
||||
IsStdout bool `mapstructure:"isStdout"`
|
||||
IsJson bool `mapstructure:"isJson"`
|
||||
IsSimplify bool `mapstructure:"isSimplify"`
|
||||
WithStack bool `mapstructure:"withStack"`
|
||||
}
|
||||
|
||||
type AllConfig struct {
|
||||
AdminAPI API
|
||||
ChatAPI API
|
||||
Admin Admin
|
||||
Chat Chat
|
||||
Discovery Discovery
|
||||
Log Log
|
||||
Mongo Mongo
|
||||
Redis Redis
|
||||
Share Share
|
||||
}
|
||||
|
||||
func (a *AllConfig) Name2Config(name string) any {
|
||||
switch name {
|
||||
case ChatAPIAdminCfgFileName:
|
||||
return a.AdminAPI
|
||||
case ChatAPIChatCfgFileName:
|
||||
return a.ChatAPI
|
||||
case ChatRPCAdminCfgFileName:
|
||||
return a.Admin
|
||||
case ChatRPCChatCfgFileName:
|
||||
return a.Chat
|
||||
case DiscoveryConfigFileName:
|
||||
return a.Discovery
|
||||
case LogConfigFileName:
|
||||
return a.Log
|
||||
case MongodbConfigFileName:
|
||||
return a.Mongo
|
||||
case RedisConfigFileName:
|
||||
return a.Redis
|
||||
case ShareFileName:
|
||||
return a.Share
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AllConfig) GetConfigNames() []string {
|
||||
return []string{
|
||||
ShareFileName,
|
||||
RedisConfigFileName,
|
||||
DiscoveryConfigFileName,
|
||||
MongodbConfigFileName,
|
||||
LogConfigFileName,
|
||||
ChatAPIAdminCfgFileName,
|
||||
ChatAPIChatCfgFileName,
|
||||
ChatRPCAdminCfgFileName,
|
||||
ChatRPCChatCfgFileName,
|
||||
}
|
||||
}
|
||||
48
pkg/common/config/env.go
Normal file
48
pkg/common/config/env.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ShareFileName = "share.yml"
|
||||
RedisConfigFileName = "redis.yml"
|
||||
DiscoveryConfigFileName = "discovery.yml"
|
||||
MongodbConfigFileName = "mongodb.yml"
|
||||
LogConfigFileName = "log.yml"
|
||||
ChatAPIAdminCfgFileName = "chat-api-admin.yml"
|
||||
ChatAPIChatCfgFileName = "chat-api-chat.yml"
|
||||
ChatAPIBotCfgFileName = "chat-api-bot.yml"
|
||||
ChatRPCAdminCfgFileName = "chat-rpc-admin.yml"
|
||||
ChatRPCChatCfgFileName = "chat-rpc-chat.yml"
|
||||
ChatRPCBotCfgFileName = "chat-rpc-bot.yml"
|
||||
)
|
||||
|
||||
var EnvPrefixMap map[string]string
|
||||
|
||||
func init() {
|
||||
EnvPrefixMap = make(map[string]string)
|
||||
fileNames := []string{
|
||||
ShareFileName,
|
||||
RedisConfigFileName,
|
||||
DiscoveryConfigFileName,
|
||||
MongodbConfigFileName,
|
||||
LogConfigFileName,
|
||||
ChatAPIAdminCfgFileName,
|
||||
ChatAPIChatCfgFileName,
|
||||
ChatRPCAdminCfgFileName,
|
||||
ChatRPCChatCfgFileName,
|
||||
}
|
||||
|
||||
for _, fileName := range fileNames {
|
||||
envKey := strings.TrimSuffix(strings.TrimSuffix(fileName, ".yml"), ".yaml")
|
||||
envKey = "CHATENV_" + envKey
|
||||
envKey = strings.ToUpper(strings.ReplaceAll(envKey, "-", "_"))
|
||||
EnvPrefixMap[fileName] = envKey
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
FlagConf = "config_folder_path"
|
||||
FlagTransferIndex = "index"
|
||||
)
|
||||
45
pkg/common/config/load.go
Normal file
45
pkg/common/config/load.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func Load(configDirectory string, configFileName string, envPrefix string, runtimeEnv string, config any) error {
|
||||
if runtimeEnv == constant.KUBERNETES {
|
||||
mountPath := os.Getenv(constant.MountConfigFilePath)
|
||||
if mountPath == "" {
|
||||
return errs.ErrArgs.WrapMsg(constant.MountConfigFilePath + " env is empty")
|
||||
}
|
||||
|
||||
return loadConfig(filepath.Join(mountPath, configFileName), envPrefix, config)
|
||||
}
|
||||
|
||||
return loadConfig(filepath.Join(configDirectory, configFileName), envPrefix, config)
|
||||
}
|
||||
|
||||
func loadConfig(path string, envPrefix string, config any) error {
|
||||
v := viper.New()
|
||||
v.SetConfigFile(path)
|
||||
v.SetEnvPrefix(envPrefix)
|
||||
v.AutomaticEnv()
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
return errs.WrapMsg(err, "failed to read config file", "path", path, "envPrefix", envPrefix)
|
||||
}
|
||||
|
||||
if err := v.Unmarshal(config, func(config *mapstructure.DecoderConfig) {
|
||||
config.TagName = "mapstructure"
|
||||
}); err != nil {
|
||||
return errs.WrapMsg(err, "failed to unmarshal config", "path", path, "envPrefix", envPrefix)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
BIN
pkg/common/config/template.xlsx
Normal file
BIN
pkg/common/config/template.xlsx
Normal file
Binary file not shown.
1
pkg/common/config/version
Normal file
1
pkg/common/config/version
Normal file
@@ -0,0 +1 @@
|
||||
v1.8.0
|
||||
128
pkg/common/constant/constant.go
Normal file
128
pkg/common/constant/constant.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package constant
|
||||
|
||||
import "git.imall.cloud/openim/protocol/constant"
|
||||
|
||||
const (
|
||||
MountConfigFilePath = "CONFIG_PATH"
|
||||
KUBERNETES = "kubernetes"
|
||||
ETCD = "etcd"
|
||||
)
|
||||
|
||||
const (
|
||||
// verificationCode used for.
|
||||
VerificationCodeForRegister = 1 // Register
|
||||
VerificationCodeForResetPassword = 2 // Reset password
|
||||
VerificationCodeForLogin = 3 // Login
|
||||
VerificationCodeForH5Register = 4 // H5 register
|
||||
)
|
||||
|
||||
const LogFileName = "chat.log"
|
||||
|
||||
// block unblock.
|
||||
const (
|
||||
BlockUser = 1
|
||||
UnblockUser = 2
|
||||
)
|
||||
|
||||
// AccountType.
|
||||
const (
|
||||
Email = "email"
|
||||
Phone = "phone"
|
||||
Account = "account"
|
||||
)
|
||||
|
||||
// Mode.
|
||||
const (
|
||||
UserMode = "user"
|
||||
AdminMode = "admin"
|
||||
)
|
||||
|
||||
const DefaultAdminLevel = 100
|
||||
|
||||
// user level.
|
||||
const (
|
||||
NormalAdmin = 80
|
||||
AdvancedUserLevel = 100
|
||||
)
|
||||
|
||||
// AddFriendCtrl.
|
||||
const (
|
||||
OrdinaryUserAddFriendEnable = 1 // Allow ordinary users to add friends
|
||||
OrdinaryUserAddFriendDisable = -1 // Do not allow ordinary users to add friends
|
||||
)
|
||||
|
||||
const (
|
||||
NormalUser = 1
|
||||
AdminUser = 2
|
||||
)
|
||||
|
||||
// mini-app
|
||||
const (
|
||||
StatusOnShelf = 1 // OnShelf
|
||||
StatusUnShelf = 2 // UnShelf
|
||||
)
|
||||
|
||||
const (
|
||||
LimitNil = 0 // None
|
||||
LimitEmpty = 1 // Neither are restricted
|
||||
LimitOnlyLoginIP = 2 // Only login is restricted
|
||||
LimitOnlyRegisterIP = 3 // Only registration is restricted
|
||||
LimitLoginIP = 4 // Restrict login
|
||||
LimitRegisterIP = 5 // Restrict registration
|
||||
LimitLoginRegisterIP = 6 // Restrict both login and registration
|
||||
)
|
||||
|
||||
const (
|
||||
InvitationCodeAll = 0 // All
|
||||
InvitationCodeUsed = 1 // Used
|
||||
InvitationCodeUnused = 2 // Unused
|
||||
)
|
||||
|
||||
const (
|
||||
RpcOpUserID = constant.OpUserID
|
||||
RpcOpUserType = "opUserType"
|
||||
)
|
||||
|
||||
const RpcCustomHeader = constant.RpcCustomHeader
|
||||
|
||||
const NeedInvitationCodeRegisterConfigKey = "needInvitationCodeRegister"
|
||||
|
||||
const (
|
||||
DefaultAllowVibration = 1
|
||||
DefaultAllowBeep = 1
|
||||
DefaultAllowAddFriend = 1
|
||||
)
|
||||
|
||||
const (
|
||||
FinDAllUser = 0
|
||||
FindNormalUser = 1
|
||||
)
|
||||
|
||||
const CtxApiToken = "api-token"
|
||||
|
||||
const (
|
||||
AccountRegister = iota
|
||||
EmailRegister
|
||||
PhoneRegister
|
||||
)
|
||||
|
||||
const (
|
||||
GenderFemale = 0 // female
|
||||
GenderMale = 1 // male
|
||||
GenderUnknown = 2 // unknown
|
||||
)
|
||||
|
||||
// Credential Type
|
||||
const (
|
||||
CredentialAccount = iota
|
||||
CredentialPhone
|
||||
CredentialEmail
|
||||
)
|
||||
|
||||
// verifyCode use
|
||||
const (
|
||||
VerifySuperCode = "supercode"
|
||||
VerifyALi = "ali"
|
||||
VerifyBao = "bao"
|
||||
VerifyMail = "mail"
|
||||
)
|
||||
21
pkg/common/constant/limit.go
Normal file
21
pkg/common/constant/limit.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 (
|
||||
ShowNumber = 1000
|
||||
StatisticsTimeInterval = 60
|
||||
MaxNotificationNum = 500
|
||||
)
|
||||
5
pkg/common/constant/user_id.go
Normal file
5
pkg/common/constant/user_id.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
AgentUserIDPrefix = "bot_"
|
||||
)
|
||||
41
pkg/common/convert/agent.go
Normal file
41
pkg/common/convert/agent.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/bot"
|
||||
pbbot "git.imall.cloud/openim/chat/pkg/protocol/bot"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
func DB2PBAgent(a *bot.Agent) *pbbot.Agent {
|
||||
return &pbbot.Agent{
|
||||
UserID: a.UserID,
|
||||
Nickname: a.NickName,
|
||||
FaceURL: a.FaceURL,
|
||||
Url: a.Url,
|
||||
Key: a.Key,
|
||||
Identity: a.Identity,
|
||||
Model: a.Model,
|
||||
Prompts: a.Prompts,
|
||||
CreateTime: a.CreateTime.UnixMilli(),
|
||||
}
|
||||
}
|
||||
|
||||
func PB2DBAgent(a *pbbot.Agent) *bot.Agent {
|
||||
return &bot.Agent{
|
||||
UserID: a.UserID,
|
||||
NickName: a.Nickname,
|
||||
FaceURL: a.FaceURL,
|
||||
Key: a.Key,
|
||||
Url: a.Url,
|
||||
Identity: a.Identity,
|
||||
Model: a.Model,
|
||||
Prompts: a.Prompts,
|
||||
CreateTime: time.UnixMilli(a.CreateTime),
|
||||
}
|
||||
}
|
||||
|
||||
func BatchDB2PBAgent(a []*bot.Agent) []*pbbot.Agent {
|
||||
return datautil.Batch(DB2PBAgent, a)
|
||||
}
|
||||
50
pkg/common/db/cache/imtoken.go
vendored
Normal file
50
pkg/common/db/cache/imtoken.go
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
const (
|
||||
chatPrefix = "CHAT:"
|
||||
imToken = chatPrefix + "IM_TOKEN:"
|
||||
)
|
||||
|
||||
func getIMTokenKey(userID string) string {
|
||||
return imToken + userID
|
||||
}
|
||||
|
||||
type IMTokenInterface interface {
|
||||
GetToken(ctx context.Context, userID string) (string, error)
|
||||
SetToken(ctx context.Context, userID, token string) error
|
||||
}
|
||||
|
||||
type imTokenCacheRedis struct {
|
||||
rdb redis.UniversalClient
|
||||
expire time.Duration
|
||||
}
|
||||
|
||||
func NewIMTokenInterface(rdb redis.UniversalClient, expire int) IMTokenInterface {
|
||||
return &imTokenCacheRedis{rdb: rdb, expire: time.Duration(expire) * time.Minute}
|
||||
}
|
||||
|
||||
func (i *imTokenCacheRedis) GetToken(ctx context.Context, userID string) (string, error) {
|
||||
key := getIMTokenKey(userID)
|
||||
token, err := i.rdb.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
return "", errs.Wrap(err)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (i *imTokenCacheRedis) SetToken(ctx context.Context, userID, token string) error {
|
||||
key := getIMTokenKey(userID)
|
||||
err := i.rdb.Set(ctx, key, token, i.expire).Err()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
84
pkg/common/db/cache/token.go
vendored
Normal file
84
pkg/common/db/cache/token.go
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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"
|
||||
|
||||
"github.com/openimsdk/tools/utils/stringutil"
|
||||
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
const (
|
||||
chatToken = "CHAT_UID_TOKEN_STATUS:"
|
||||
)
|
||||
|
||||
type TokenInterface interface {
|
||||
AddTokenFlag(ctx context.Context, userID string, token string, flag int) error
|
||||
AddTokenFlagNXEx(ctx context.Context, userID string, token string, flag int, expire time.Duration) (bool, error)
|
||||
GetTokensWithoutError(ctx context.Context, userID string) (map[string]int32, error)
|
||||
DeleteTokenByUid(ctx context.Context, userID string) error
|
||||
}
|
||||
|
||||
type TokenCacheRedis struct {
|
||||
rdb redis.UniversalClient
|
||||
accessExpire int64
|
||||
}
|
||||
|
||||
func NewTokenInterface(rdb redis.UniversalClient) *TokenCacheRedis {
|
||||
return &TokenCacheRedis{rdb: rdb}
|
||||
}
|
||||
|
||||
func (t *TokenCacheRedis) AddTokenFlag(ctx context.Context, userID string, token string, flag int) error {
|
||||
key := chatToken + userID
|
||||
return errs.Wrap(t.rdb.HSet(ctx, key, token, flag).Err())
|
||||
}
|
||||
|
||||
func (t *TokenCacheRedis) AddTokenFlagNXEx(ctx context.Context, userID string, token string, flag int, expire time.Duration) (bool, error) {
|
||||
key := chatToken + userID
|
||||
isSet, err := t.rdb.HSetNX(ctx, key, token, flag).Result()
|
||||
if err != nil {
|
||||
return false, errs.Wrap(err)
|
||||
}
|
||||
if !isSet {
|
||||
// key already exists
|
||||
return false, nil
|
||||
}
|
||||
if err = t.rdb.Expire(ctx, key, expire).Err(); err != nil {
|
||||
return false, errs.Wrap(err)
|
||||
}
|
||||
return isSet, nil
|
||||
}
|
||||
|
||||
func (t *TokenCacheRedis) GetTokensWithoutError(ctx context.Context, userID string) (map[string]int32, error) {
|
||||
key := chatToken + userID
|
||||
m, err := t.rdb.HGetAll(ctx, key).Result()
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
mm := make(map[string]int32)
|
||||
for k, v := range m {
|
||||
mm[k] = stringutil.StringToInt32(v)
|
||||
}
|
||||
return mm, nil
|
||||
}
|
||||
|
||||
func (t *TokenCacheRedis) DeleteTokenByUid(ctx context.Context, userID string) error {
|
||||
key := chatToken + userID
|
||||
return errs.Wrap(t.rdb.Del(ctx, key).Err())
|
||||
}
|
||||
86
pkg/common/db/cache/user_login_ip.go
vendored
Normal file
86
pkg/common/db/cache/user_login_ip.go
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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"
|
||||
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
const (
|
||||
userLoginIPPrefix = "CHAT:USER_LOGIN_IP:"
|
||||
// 缓存过期时间:7天(用户登录IP变化不频繁,可以设置较长的过期时间)
|
||||
userLoginIPExpire = 7 * 24 * time.Hour
|
||||
)
|
||||
|
||||
func getUserLoginIPKey(userID string) string {
|
||||
return userLoginIPPrefix + userID
|
||||
}
|
||||
|
||||
type UserLoginIPInterface interface {
|
||||
// GetLatestLoginIP 获取用户最新登录IP(从缓存)
|
||||
// 返回值:(ip, found, error)
|
||||
// - ip: 缓存的IP值(如果found为false,则ip为空字符串)
|
||||
// - found: 是否在缓存中找到(true表示缓存命中,false表示缓存未命中)
|
||||
// - error: 错误信息
|
||||
GetLatestLoginIP(ctx context.Context, userID string) (string, bool, error)
|
||||
// SetLatestLoginIP 设置用户最新登录IP到缓存
|
||||
SetLatestLoginIP(ctx context.Context, userID, ip string) error
|
||||
// DeleteLatestLoginIP 删除用户最新登录IP缓存(用于保证缓存一致性)
|
||||
DeleteLatestLoginIP(ctx context.Context, userID string) error
|
||||
}
|
||||
|
||||
type userLoginIPCacheRedis struct {
|
||||
rdb redis.UniversalClient
|
||||
}
|
||||
|
||||
func NewUserLoginIPInterface(rdb redis.UniversalClient) UserLoginIPInterface {
|
||||
return &userLoginIPCacheRedis{rdb: rdb}
|
||||
}
|
||||
|
||||
func (u *userLoginIPCacheRedis) GetLatestLoginIP(ctx context.Context, userID string) (string, bool, error) {
|
||||
key := getUserLoginIPKey(userID)
|
||||
ip, err := u.rdb.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
// 缓存不存在,返回 false 表示缓存未命中
|
||||
return "", false, nil
|
||||
}
|
||||
return "", false, errs.Wrap(err)
|
||||
}
|
||||
// 缓存命中,返回 true
|
||||
return ip, true, nil
|
||||
}
|
||||
|
||||
func (u *userLoginIPCacheRedis) SetLatestLoginIP(ctx context.Context, userID, ip string) error {
|
||||
key := getUserLoginIPKey(userID)
|
||||
err := u.rdb.Set(ctx, key, ip, userLoginIPExpire).Err()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *userLoginIPCacheRedis) DeleteLatestLoginIP(ctx context.Context, userID string) error {
|
||||
key := getUserLoginIPKey(userID)
|
||||
err := u.rdb.Del(ctx, key).Err()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
407
pkg/common/db/database/admin.go
Normal file
407
pkg/common/db/database/admin.go
Normal file
@@ -0,0 +1,407 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/cache"
|
||||
"git.imall.cloud/openim/protocol/constant"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/db/tx"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/model/admin"
|
||||
admindb "git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
)
|
||||
|
||||
type AdminDatabaseInterface interface {
|
||||
GetAdmin(ctx context.Context, account string) (*admindb.Admin, error)
|
||||
GetAdminUserID(ctx context.Context, userID string) (*admindb.Admin, error)
|
||||
UpdateAdmin(ctx context.Context, userID string, update map[string]any) error
|
||||
ChangePassword(ctx context.Context, userID string, newPassword string) error
|
||||
ChangeOperationPassword(ctx context.Context, userID string, newPassword string) error
|
||||
ClearGoogleAuthKey(ctx context.Context, userID string) error
|
||||
AddAdminAccount(ctx context.Context, admin []*admindb.Admin) error
|
||||
DelAdminAccount(ctx context.Context, userIDs []string) error
|
||||
SearchAdminAccount(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.Admin, error)
|
||||
CreateApplet(ctx context.Context, applets []*admindb.Applet) error
|
||||
DelApplet(ctx context.Context, appletIDs []string) error
|
||||
GetApplet(ctx context.Context, appletID string) (*admindb.Applet, error)
|
||||
FindApplet(ctx context.Context, appletIDs []string) ([]*admindb.Applet, error)
|
||||
SearchApplet(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.Applet, error)
|
||||
FindOnShelf(ctx context.Context) ([]*admindb.Applet, error)
|
||||
UpdateApplet(ctx context.Context, appletID string, update map[string]any) error
|
||||
GetConfig(ctx context.Context) (map[string]string, error)
|
||||
SetConfig(ctx context.Context, cs map[string]string) error
|
||||
DelConfig(ctx context.Context, keys []string) error
|
||||
FindInvitationRegister(ctx context.Context, codes []string) ([]*admindb.InvitationRegister, error)
|
||||
DelInvitationRegister(ctx context.Context, codes []string) error
|
||||
UpdateInvitationRegister(ctx context.Context, code string, fields map[string]any) error
|
||||
CreatInvitationRegister(ctx context.Context, invitationRegisters []*admindb.InvitationRegister) error
|
||||
SearchInvitationRegister(ctx context.Context, keyword string, state int32, userIDs []string, codes []string, pagination pagination.Pagination) (int64, []*admindb.InvitationRegister, error)
|
||||
SearchIPForbidden(ctx context.Context, keyword string, state int32, pagination pagination.Pagination) (int64, []*admindb.IPForbidden, error)
|
||||
AddIPForbidden(ctx context.Context, ms []*admindb.IPForbidden) error
|
||||
FindIPForbidden(ctx context.Context, ms []string) ([]*admindb.IPForbidden, error)
|
||||
DelIPForbidden(ctx context.Context, ips []string) error
|
||||
FindDefaultFriend(ctx context.Context, userIDs []string) ([]string, error)
|
||||
AddDefaultFriend(ctx context.Context, ms []*admindb.RegisterAddFriend) error
|
||||
DelDefaultFriend(ctx context.Context, userIDs []string) error
|
||||
SearchDefaultFriend(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.RegisterAddFriend, error)
|
||||
FindDefaultGroup(ctx context.Context, groupIDs []string) ([]string, error)
|
||||
AddDefaultGroup(ctx context.Context, ms []*admindb.RegisterAddGroup) error
|
||||
DelDefaultGroup(ctx context.Context, groupIDs []string) error
|
||||
SearchDefaultGroup(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.RegisterAddGroup, error)
|
||||
CountTotalGroups(ctx context.Context) (int64, error) // 统计群组总数
|
||||
CountTodayNewGroups(ctx context.Context) (int64, error) // 统计今天新建的群组数
|
||||
CountTotalFriends(ctx context.Context) (int64, error) // 统计好友总数
|
||||
FindBlockInfo(ctx context.Context, userIDs []string) ([]*admindb.ForbiddenAccount, error)
|
||||
GetBlockInfo(ctx context.Context, userID string) (*admindb.ForbiddenAccount, error)
|
||||
BlockUser(ctx context.Context, f []*admindb.ForbiddenAccount) error
|
||||
DelBlockUser(ctx context.Context, userID []string) error
|
||||
SearchBlockUser(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.ForbiddenAccount, error)
|
||||
FindBlockUser(ctx context.Context, userIDs []string) ([]*admindb.ForbiddenAccount, error)
|
||||
SearchUserLimitLogin(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.LimitUserLoginIP, error)
|
||||
AddUserLimitLogin(ctx context.Context, ms []*admindb.LimitUserLoginIP) error
|
||||
DelUserLimitLogin(ctx context.Context, ms []*admindb.LimitUserLoginIP) error
|
||||
CountLimitUserLoginIP(ctx context.Context, userID string) (uint32, error)
|
||||
GetLimitUserLoginIP(ctx context.Context, userID string, ip string) (*admindb.LimitUserLoginIP, error)
|
||||
CacheToken(ctx context.Context, userID string, token string, expire time.Duration) error
|
||||
GetTokens(ctx context.Context, userID string) (map[string]int32, error)
|
||||
DeleteToken(ctx context.Context, userID string) error
|
||||
LatestVersion(ctx context.Context, platform string) (*admindb.Application, error)
|
||||
AddVersion(ctx context.Context, val *admindb.Application) error
|
||||
UpdateVersion(ctx context.Context, id primitive.ObjectID, update map[string]any) error
|
||||
DeleteVersion(ctx context.Context, id []primitive.ObjectID) error
|
||||
PageVersion(ctx context.Context, platforms []string, page pagination.Pagination) (int64, []*admindb.Application, error)
|
||||
}
|
||||
|
||||
func NewAdminDatabase(cli *mongoutil.Client, rdb redis.UniversalClient) (AdminDatabaseInterface, error) {
|
||||
a, err := admin.NewAdmin(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
forbidden, err := admin.NewIPForbidden(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
forbiddenAccount, err := admin.NewForbiddenAccount(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
limitUserLoginIP, err := admin.NewLimitUserLoginIP(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invitationRegister, err := admin.NewInvitationRegister(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
registerAddFriend, err := admin.NewRegisterAddFriend(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
registerAddGroup, err := admin.NewRegisterAddGroup(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applet, err := admin.NewApplet(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientConfig, err := admin.NewClientConfig(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
application, err := admin.NewApplication(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AdminDatabase{
|
||||
tx: cli.GetTx(),
|
||||
admin: a,
|
||||
ipForbidden: forbidden,
|
||||
forbiddenAccount: forbiddenAccount,
|
||||
limitUserLoginIP: limitUserLoginIP,
|
||||
invitationRegister: invitationRegister,
|
||||
registerAddFriend: registerAddFriend,
|
||||
registerAddGroup: registerAddGroup,
|
||||
applet: applet,
|
||||
clientConfig: clientConfig,
|
||||
application: application,
|
||||
cache: cache.NewTokenInterface(rdb),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type AdminDatabase struct {
|
||||
tx tx.Tx
|
||||
admin admindb.AdminInterface
|
||||
ipForbidden admindb.IPForbiddenInterface
|
||||
forbiddenAccount admindb.ForbiddenAccountInterface
|
||||
limitUserLoginIP admindb.LimitUserLoginIPInterface
|
||||
invitationRegister admindb.InvitationRegisterInterface
|
||||
registerAddFriend admindb.RegisterAddFriendInterface
|
||||
registerAddGroup admindb.RegisterAddGroupInterface
|
||||
applet admindb.AppletInterface
|
||||
clientConfig admindb.ClientConfigInterface
|
||||
application admindb.ApplicationInterface
|
||||
cache cache.TokenInterface
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) GetAdmin(ctx context.Context, account string) (*admindb.Admin, error) {
|
||||
return o.admin.Take(ctx, account)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) GetAdminUserID(ctx context.Context, userID string) (*admindb.Admin, error) {
|
||||
return o.admin.TakeUserID(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) UpdateAdmin(ctx context.Context, userID string, update map[string]any) error {
|
||||
return o.admin.Update(ctx, userID, update)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) ChangePassword(ctx context.Context, userID string, newPassword string) error {
|
||||
return o.admin.ChangePassword(ctx, userID, newPassword)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) ChangeOperationPassword(ctx context.Context, userID string, newPassword string) error {
|
||||
return o.admin.ChangeOperationPassword(ctx, userID, newPassword)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) ClearGoogleAuthKey(ctx context.Context, userID string) error {
|
||||
return o.admin.ClearGoogleAuthKey(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) AddAdminAccount(ctx context.Context, admins []*admindb.Admin) error {
|
||||
return o.admin.Create(ctx, admins)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelAdminAccount(ctx context.Context, userIDs []string) error {
|
||||
return o.admin.Delete(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SearchAdminAccount(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.Admin, error) {
|
||||
return o.admin.Search(ctx, keyword, pagination)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) CreateApplet(ctx context.Context, applets []*admindb.Applet) error {
|
||||
return o.applet.Create(ctx, applets)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelApplet(ctx context.Context, appletIDs []string) error {
|
||||
return o.applet.Del(ctx, appletIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) GetApplet(ctx context.Context, appletID string) (*admindb.Applet, error) {
|
||||
return o.applet.Take(ctx, appletID)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) FindApplet(ctx context.Context, appletIDs []string) ([]*admindb.Applet, error) {
|
||||
return o.applet.FindID(ctx, appletIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SearchApplet(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.Applet, error) {
|
||||
return o.applet.Search(ctx, keyword, pagination)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) FindOnShelf(ctx context.Context) ([]*admindb.Applet, error) {
|
||||
return o.applet.FindOnShelf(ctx)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) UpdateApplet(ctx context.Context, appletID string, update map[string]any) error {
|
||||
return o.applet.Update(ctx, appletID, update)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) GetConfig(ctx context.Context) (map[string]string, error) {
|
||||
return o.clientConfig.Get(ctx)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SetConfig(ctx context.Context, cs map[string]string) error {
|
||||
return o.clientConfig.Set(ctx, cs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelConfig(ctx context.Context, keys []string) error {
|
||||
return o.clientConfig.Del(ctx, keys)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) FindInvitationRegister(ctx context.Context, codes []string) ([]*admindb.InvitationRegister, error) {
|
||||
return o.invitationRegister.Find(ctx, codes)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelInvitationRegister(ctx context.Context, codes []string) error {
|
||||
return o.invitationRegister.Del(ctx, codes)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) UpdateInvitationRegister(ctx context.Context, code string, fields map[string]any) error {
|
||||
return o.invitationRegister.Update(ctx, code, fields)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) CreatInvitationRegister(ctx context.Context, invitationRegisters []*admindb.InvitationRegister) error {
|
||||
return o.invitationRegister.Create(ctx, invitationRegisters)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SearchInvitationRegister(ctx context.Context, keyword string, state int32, userIDs []string, codes []string, pagination pagination.Pagination) (int64, []*admindb.InvitationRegister, error) {
|
||||
return o.invitationRegister.Search(ctx, keyword, state, userIDs, codes, pagination)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SearchIPForbidden(ctx context.Context, keyword string, state int32, pagination pagination.Pagination) (int64, []*admindb.IPForbidden, error) {
|
||||
return o.ipForbidden.Search(ctx, keyword, state, pagination)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) AddIPForbidden(ctx context.Context, ms []*admindb.IPForbidden) error {
|
||||
return o.ipForbidden.Create(ctx, ms)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) FindIPForbidden(ctx context.Context, ms []string) ([]*admindb.IPForbidden, error) {
|
||||
return o.ipForbidden.Find(ctx, ms)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelIPForbidden(ctx context.Context, ips []string) error {
|
||||
return o.ipForbidden.Delete(ctx, ips)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) FindDefaultFriend(ctx context.Context, userIDs []string) ([]string, error) {
|
||||
return o.registerAddFriend.FindUserID(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) AddDefaultFriend(ctx context.Context, ms []*admindb.RegisterAddFriend) error {
|
||||
return o.registerAddFriend.Add(ctx, ms)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelDefaultFriend(ctx context.Context, userIDs []string) error {
|
||||
return o.registerAddFriend.Del(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SearchDefaultFriend(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.RegisterAddFriend, error) {
|
||||
return o.registerAddFriend.Search(ctx, keyword, pagination)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) FindDefaultGroup(ctx context.Context, groupIDs []string) ([]string, error) {
|
||||
return o.registerAddGroup.FindGroupID(ctx, groupIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) AddDefaultGroup(ctx context.Context, ms []*admindb.RegisterAddGroup) error {
|
||||
return o.registerAddGroup.Add(ctx, ms)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelDefaultGroup(ctx context.Context, groupIDs []string) error {
|
||||
return o.registerAddGroup.Del(ctx, groupIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SearchDefaultGroup(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.RegisterAddGroup, error) {
|
||||
return o.registerAddGroup.Search(ctx, keyword, pagination)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) CountTotalGroups(ctx context.Context) (int64, error) {
|
||||
return o.registerAddGroup.CountTotal(ctx)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) CountTodayNewGroups(ctx context.Context) (int64, error) {
|
||||
return o.registerAddGroup.CountToday(ctx)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) CountTotalFriends(ctx context.Context) (int64, error) {
|
||||
return o.registerAddFriend.CountTotal(ctx)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) FindBlockInfo(ctx context.Context, userIDs []string) ([]*admindb.ForbiddenAccount, error) {
|
||||
return o.forbiddenAccount.Find(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) GetBlockInfo(ctx context.Context, userID string) (*admindb.ForbiddenAccount, error) {
|
||||
return o.forbiddenAccount.Take(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) BlockUser(ctx context.Context, f []*admindb.ForbiddenAccount) error {
|
||||
return o.forbiddenAccount.Create(ctx, f)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelBlockUser(ctx context.Context, userID []string) error {
|
||||
return o.forbiddenAccount.Delete(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SearchBlockUser(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.ForbiddenAccount, error) {
|
||||
return o.forbiddenAccount.Search(ctx, keyword, pagination)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) FindBlockUser(ctx context.Context, userIDs []string) ([]*admindb.ForbiddenAccount, error) {
|
||||
return o.forbiddenAccount.Find(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) SearchUserLimitLogin(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.LimitUserLoginIP, error) {
|
||||
return o.limitUserLoginIP.Search(ctx, keyword, pagination)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) AddUserLimitLogin(ctx context.Context, ms []*admindb.LimitUserLoginIP) error {
|
||||
return o.limitUserLoginIP.Create(ctx, ms)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DelUserLimitLogin(ctx context.Context, ms []*admindb.LimitUserLoginIP) error {
|
||||
return o.limitUserLoginIP.Delete(ctx, ms)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) CountLimitUserLoginIP(ctx context.Context, userID string) (uint32, error) {
|
||||
return o.limitUserLoginIP.Count(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) GetLimitUserLoginIP(ctx context.Context, userID string, ip string) (*admindb.LimitUserLoginIP, error) {
|
||||
return o.limitUserLoginIP.Take(ctx, userID, ip)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) CacheToken(ctx context.Context, userID string, token string, expire time.Duration) error {
|
||||
isSet, err := o.cache.AddTokenFlagNXEx(ctx, userID, token, constant.NormalToken, expire)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isSet {
|
||||
// already exists, update
|
||||
if err = o.cache.AddTokenFlag(ctx, userID, token, constant.NormalToken); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) GetTokens(ctx context.Context, userID string) (map[string]int32, error) {
|
||||
return o.cache.GetTokensWithoutError(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DeleteToken(ctx context.Context, userID string) error {
|
||||
return o.cache.DeleteTokenByUid(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) LatestVersion(ctx context.Context, platform string) (*admindb.Application, error) {
|
||||
return o.application.LatestVersion(ctx, platform)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) AddVersion(ctx context.Context, val *admindb.Application) error {
|
||||
return o.application.AddVersion(ctx, val)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) UpdateVersion(ctx context.Context, id primitive.ObjectID, update map[string]any) error {
|
||||
return o.application.UpdateVersion(ctx, id, update)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) DeleteVersion(ctx context.Context, id []primitive.ObjectID) error {
|
||||
return o.application.DeleteVersion(ctx, id)
|
||||
}
|
||||
|
||||
func (o *AdminDatabase) PageVersion(ctx context.Context, platforms []string, page pagination.Pagination) (int64, []*admindb.Application, error) {
|
||||
return o.application.PageVersion(ctx, platforms, page)
|
||||
}
|
||||
77
pkg/common/db/database/bot.go
Normal file
77
pkg/common/db/database/bot.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/model/bot"
|
||||
tablebot "git.imall.cloud/openim/chat/pkg/common/db/table/bot"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/db/tx"
|
||||
)
|
||||
|
||||
type BotDatabase interface {
|
||||
CreateAgent(ctx context.Context, jobs ...*tablebot.Agent) error
|
||||
TakeAgent(ctx context.Context, userID string) (*tablebot.Agent, error)
|
||||
FindAgents(ctx context.Context, userIDs []string) ([]*tablebot.Agent, error)
|
||||
UpdateAgent(ctx context.Context, userID string, data map[string]any) error
|
||||
DeleteAgents(ctx context.Context, userIDs []string) error
|
||||
PageAgents(ctx context.Context, userIDs []string, pagination pagination.Pagination) (int64, []*tablebot.Agent, error)
|
||||
|
||||
TakeConversationRespID(ctx context.Context, convID, agentID string) (*tablebot.ConversationRespID, error)
|
||||
UpdateConversationRespID(ctx context.Context, convID, agentID string, data map[string]any) error
|
||||
}
|
||||
|
||||
type botDatabase struct {
|
||||
tx tx.Tx
|
||||
agent tablebot.AgentInterface
|
||||
convRespID tablebot.ConversationRespIDInterface
|
||||
}
|
||||
|
||||
func NewBotDatabase(cli *mongoutil.Client) (BotDatabase, error) {
|
||||
agent, err := bot.NewAgent(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
convRespID, err := bot.NewConversationRespID(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &botDatabase{
|
||||
tx: cli.GetTx(),
|
||||
agent: agent,
|
||||
convRespID: convRespID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *botDatabase) CreateAgent(ctx context.Context, agents ...*tablebot.Agent) error {
|
||||
return a.agent.Create(ctx, agents...)
|
||||
}
|
||||
|
||||
func (a *botDatabase) TakeAgent(ctx context.Context, userID string) (*tablebot.Agent, error) {
|
||||
return a.agent.Take(ctx, userID)
|
||||
}
|
||||
|
||||
func (a *botDatabase) FindAgents(ctx context.Context, userIDs []string) ([]*tablebot.Agent, error) {
|
||||
return a.agent.Find(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (a *botDatabase) UpdateAgent(ctx context.Context, userID string, data map[string]any) error {
|
||||
return a.agent.Update(ctx, userID, data)
|
||||
}
|
||||
|
||||
func (a *botDatabase) DeleteAgents(ctx context.Context, userIDs []string) error {
|
||||
return a.agent.Delete(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (a *botDatabase) PageAgents(ctx context.Context, userIDs []string, pagination pagination.Pagination) (int64, []*tablebot.Agent, error) {
|
||||
return a.agent.Page(ctx, userIDs, pagination)
|
||||
}
|
||||
|
||||
func (a *botDatabase) UpdateConversationRespID(ctx context.Context, convID, agentID string, data map[string]any) error {
|
||||
return a.convRespID.Update(ctx, convID, agentID, data)
|
||||
}
|
||||
|
||||
func (a *botDatabase) TakeConversationRespID(ctx context.Context, convID, agentID string) (*tablebot.ConversationRespID, error) {
|
||||
return a.convRespID.Take(ctx, convID, agentID)
|
||||
}
|
||||
867
pkg/common/db/database/chat.go
Normal file
867
pkg/common/db/database/chat.go
Normal file
@@ -0,0 +1,867 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/db/tx"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/cache"
|
||||
admindb "git.imall.cloud/openim/chat/pkg/common/db/model/admin"
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/model/chat"
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
chatdb "git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
type ChatDatabaseInterface interface {
|
||||
GetUser(ctx context.Context, userID string) (account *chatdb.Account, err error)
|
||||
UpdateUseInfo(ctx context.Context, userID string, attribute map[string]any, updateCred, delCred []*chatdb.Credential) (err error)
|
||||
FindAttribute(ctx context.Context, userIDs []string) ([]*chatdb.Attribute, error)
|
||||
FindAttributeByAccount(ctx context.Context, accounts []string) ([]*chatdb.Attribute, error)
|
||||
TakeAttributeByPhone(ctx context.Context, areaCode string, phoneNumber string) (*chatdb.Attribute, error)
|
||||
TakeAttributeByEmail(ctx context.Context, Email string) (*chatdb.Attribute, error)
|
||||
TakeAttributeByAccount(ctx context.Context, account string) (*chatdb.Attribute, error)
|
||||
TakeAttributeByUserID(ctx context.Context, userID string) (*chatdb.Attribute, error)
|
||||
TakeAccount(ctx context.Context, userID string) (*chatdb.Account, error)
|
||||
TakeCredentialByAccount(ctx context.Context, account string) (*chatdb.Credential, error)
|
||||
TakeCredentialsByUserID(ctx context.Context, userID string) ([]*chatdb.Credential, error)
|
||||
TakeLastVerifyCode(ctx context.Context, account string) (*chatdb.VerifyCode, error)
|
||||
Search(ctx context.Context, normalUser int32, keyword string, genders int32, startTime, endTime *time.Time, pagination pagination.Pagination) (int64, []*chatdb.Attribute, error)
|
||||
SearchWithRealNameAuth(ctx context.Context, normalUser int32, keyword string, genders int32, startTime, endTime *time.Time, realNameKeyword string, idCardKeyword string, pagination pagination.Pagination) (total int64, attributes []*chatdb.Attribute, err error) // 搜索用户(支持实名信息搜索)
|
||||
SearchUser(ctx context.Context, keyword string, userIDs []string, genders []int32, pagination pagination.Pagination) (int64, []*chatdb.Attribute, error)
|
||||
CountVerifyCodeRange(ctx context.Context, account string, start time.Time, end time.Time) (int64, error)
|
||||
AddVerifyCode(ctx context.Context, verifyCode *chatdb.VerifyCode, fn func() error) error
|
||||
UpdateVerifyCodeIncrCount(ctx context.Context, id string) error
|
||||
DelVerifyCode(ctx context.Context, id string) error
|
||||
RegisterUser(ctx context.Context, register *chatdb.Register, account *chatdb.Account, attribute *chatdb.Attribute, credentials []*chatdb.Credential) error
|
||||
LoginRecord(ctx context.Context, record *chatdb.UserLoginRecord, verifyCodeID *string) error
|
||||
UpdatePassword(ctx context.Context, userID string, password string) error
|
||||
UpdatePasswordAndDeleteVerifyCode(ctx context.Context, userID string, password string, codeID string) error
|
||||
NewUserCountTotal(ctx context.Context, before *time.Time) (int64, error)
|
||||
UserLoginCountTotal(ctx context.Context, before *time.Time) (int64, error)
|
||||
UserLoginCountRangeEverydayTotal(ctx context.Context, start *time.Time, end *time.Time) (map[string]int64, int64, error)
|
||||
CountTodayRegisteredUsers(ctx context.Context) (int64, error)
|
||||
CountTodayActiveUsers(ctx context.Context) (int64, error)
|
||||
GetLatestLoginIP(ctx context.Context, userID string) (string, error) // 获取用户最新登录IP
|
||||
SearchUserLoginRecords(ctx context.Context, userID, ip string, pagination pagination.Pagination) (int64, []*chatdb.UserLoginRecord, error) // 查询用户登录记录(支持按用户ID或IP查询)
|
||||
DelUserAccount(ctx context.Context, userIDs []string) error
|
||||
|
||||
// 敏感词相关方法
|
||||
GetSensitiveWords(ctx context.Context) ([]*chatdb.SensitiveWord, error)
|
||||
CheckSensitiveWords(ctx context.Context, content string) ([]*chatdb.SensitiveWord, bool, error)
|
||||
FilterContent(ctx context.Context, content string) (string, []*chatdb.SensitiveWord, bool, error)
|
||||
GetSensitiveWordConfig(ctx context.Context) (*chatdb.SensitiveWordConfig, error)
|
||||
|
||||
// 敏感词管理相关方法
|
||||
CreateSensitiveWord(ctx context.Context, word *chatdb.SensitiveWord) error
|
||||
UpdateSensitiveWord(ctx context.Context, id string, data map[string]any) error
|
||||
DeleteSensitiveWord(ctx context.Context, ids []string) error
|
||||
GetSensitiveWord(ctx context.Context, id string) (*chatdb.SensitiveWord, error)
|
||||
SearchSensitiveWords(ctx context.Context, keyword string, action int32, status int32, pagination pagination.Pagination) (int64, []*chatdb.SensitiveWord, error)
|
||||
BatchAddSensitiveWords(ctx context.Context, words []*chatdb.SensitiveWord) error
|
||||
BatchUpdateSensitiveWords(ctx context.Context, updates map[string]map[string]any) error
|
||||
BatchDeleteSensitiveWords(ctx context.Context, ids []string) error
|
||||
|
||||
// 敏感词分组管理
|
||||
CreateSensitiveWordGroup(ctx context.Context, group *chatdb.SensitiveWordGroup) error
|
||||
UpdateSensitiveWordGroup(ctx context.Context, id string, data map[string]any) error
|
||||
DeleteSensitiveWordGroup(ctx context.Context, ids []string) error
|
||||
GetSensitiveWordGroup(ctx context.Context, id string) (*chatdb.SensitiveWordGroup, error)
|
||||
GetAllSensitiveWordGroups(ctx context.Context) ([]*chatdb.SensitiveWordGroup, error)
|
||||
|
||||
// 敏感词配置管理
|
||||
UpdateSensitiveWordConfig(ctx context.Context, config *chatdb.SensitiveWordConfig) error
|
||||
|
||||
// 敏感词日志管理
|
||||
GetSensitiveWordLogs(ctx context.Context, userID, groupID string, pagination pagination.Pagination) (int64, []*chatdb.SensitiveWordLog, error)
|
||||
DeleteSensitiveWordLogs(ctx context.Context, ids []string) error
|
||||
|
||||
// 敏感词统计
|
||||
GetSensitiveWordStats(ctx context.Context) (map[string]int64, error)
|
||||
GetSensitiveWordLogStats(ctx context.Context, startTime, endTime time.Time) (map[string]int64, error)
|
||||
|
||||
// 收藏相关方法
|
||||
CreateFavorite(ctx context.Context, favorite *chatdb.Favorite) error
|
||||
GetFavorite(ctx context.Context, favoriteID string) (*chatdb.Favorite, error)
|
||||
GetFavoritesByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.Favorite, error)
|
||||
GetFavoritesByUserIDAndType(ctx context.Context, userID string, favoriteType int32, pagination pagination.Pagination) (int64, []*chatdb.Favorite, error)
|
||||
SearchFavoritesByKeyword(ctx context.Context, userID string, keyword string, pagination pagination.Pagination) (int64, []*chatdb.Favorite, error)
|
||||
UpdateFavorite(ctx context.Context, favoriteID string, data map[string]any) error
|
||||
DeleteFavorite(ctx context.Context, favoriteIDs []string) error
|
||||
DeleteFavoritesByUserID(ctx context.Context, userID string) error
|
||||
CountFavoritesByUserID(ctx context.Context, userID string) (int64, error)
|
||||
GetFavoritesByTags(ctx context.Context, userID string, tags []string, pagination pagination.Pagination) (int64, []*chatdb.Favorite, error)
|
||||
|
||||
// 定时任务相关方法
|
||||
CreateScheduledTask(ctx context.Context, task *chatdb.ScheduledTask) error
|
||||
GetScheduledTask(ctx context.Context, taskID string) (*chatdb.ScheduledTask, error)
|
||||
GetScheduledTasksByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.ScheduledTask, error)
|
||||
GetAllScheduledTasks(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.ScheduledTask, error)
|
||||
UpdateScheduledTask(ctx context.Context, taskID string, data map[string]any) error
|
||||
DeleteScheduledTask(ctx context.Context, taskIDs []string) error
|
||||
|
||||
// 系统配置相关方法
|
||||
CreateSystemConfig(ctx context.Context, config *chatdb.SystemConfig) error
|
||||
GetSystemConfig(ctx context.Context, key string) (*chatdb.SystemConfig, error)
|
||||
GetSystemConfigsByKeys(ctx context.Context, keys []string) ([]*chatdb.SystemConfig, error)
|
||||
GetAllSystemConfigs(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.SystemConfig, error)
|
||||
UpdateSystemConfig(ctx context.Context, key string, data map[string]any) error
|
||||
UpdateSystemConfigValue(ctx context.Context, key string, value string) error
|
||||
UpdateSystemConfigEnabled(ctx context.Context, key string, enabled bool) error
|
||||
DeleteSystemConfig(ctx context.Context, keys []string) error
|
||||
GetEnabledSystemConfigs(ctx context.Context) ([]*chatdb.SystemConfig, error)
|
||||
GetAppSystemConfigs(ctx context.Context) ([]*chatdb.SystemConfig, error) // 获取所有 show_in_app=true 且 enabled=true 的配置
|
||||
|
||||
// 钱包相关方法
|
||||
GetWallet(ctx context.Context, userID string) (*chatdb.Wallet, error) // 获取钱包信息
|
||||
GetWalletsByUserIDs(ctx context.Context, userIDs []string) ([]*chatdb.Wallet, error) // 根据用户ID列表批量获取钱包
|
||||
GetWalletsPage(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.Wallet, error) // 分页获取钱包列表
|
||||
GetWalletsPageByRealNameAuthAuditStatus(ctx context.Context, auditStatus int32, userID string, pagination pagination.Pagination) (int64, []*chatdb.Wallet, error) // 按实名认证审核状态分页查询钱包(支持用户ID搜索)
|
||||
CreateWallet(ctx context.Context, wallet *chatdb.Wallet) error // 创建钱包
|
||||
UpdateWalletBalance(ctx context.Context, userID string, balance int64) error // 更新钱包余额
|
||||
IncrementWalletBalance(ctx context.Context, userID string, amount int64) (beforeBalance int64, afterBalance int64, err error) // 原子更新余额,返回更新前后的余额
|
||||
UpdateWalletPaymentPassword(ctx context.Context, userID string, paymentPassword string) error // 更新支付密码
|
||||
UpdateWalletWithdrawAccount(ctx context.Context, userID string, withdrawAccount string) error // 更新提款账号(兼容旧接口)
|
||||
UpdateWalletWithdrawAccountWithType(ctx context.Context, userID string, withdrawAccount string, accountType int32) error // 更新提款账号(带类型)
|
||||
UpdateWalletRealNameAuth(ctx context.Context, userID string, realNameAuth chatdb.RealNameAuth) error // 更新实名认证信息
|
||||
GetWalletBalanceRecords(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.WalletBalanceRecord, error) // 获取钱包余额变动记录
|
||||
GetWalletBalanceRecordsByUserIDAndType(ctx context.Context, userID string, recordType int32, pagination pagination.Pagination) (int64, []*chatdb.WalletBalanceRecord, error) // 根据类型获取钱包余额变动记录
|
||||
CreateWalletBalanceRecord(ctx context.Context, record *chatdb.WalletBalanceRecord) error // 创建余额变动记录
|
||||
|
||||
// 提现相关方法
|
||||
CreateWithdraw(ctx context.Context, withdraw *chatdb.Withdraw) error // 创建提现记录
|
||||
GetWithdraw(ctx context.Context, withdrawID string) (*chatdb.Withdraw, error) // 获取提现记录
|
||||
GetWithdrawsByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) // 获取用户的提现记录列表
|
||||
GetWithdrawsByStatus(ctx context.Context, status int32, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) // 根据状态获取提现记录列表
|
||||
UpdateWithdrawStatus(ctx context.Context, withdrawID string, status int32, auditorID string, auditRemark string) error // 更新提现状态(审核)
|
||||
GetWithdrawsPage(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) // 获取提现记录列表(分页)
|
||||
|
||||
// 提现申请相关方法
|
||||
CreateWithdrawApplication(ctx context.Context, application *chatdb.WithdrawApplication) error // 创建提现申请
|
||||
GetWithdrawApplication(ctx context.Context, applicationID string) (*chatdb.WithdrawApplication, error) // 获取提现申请
|
||||
GetWithdrawApplicationsByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) // 获取用户的提现申请列表
|
||||
GetWithdrawApplicationsByStatus(ctx context.Context, status int32, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) // 根据状态获取提现申请列表
|
||||
UpdateWithdrawApplicationStatus(ctx context.Context, applicationID string, status int32, auditorID string, auditRemark string) error // 更新提现申请状态(审核)
|
||||
GetWithdrawApplicationsPage(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) // 获取提现申请列表(分页)
|
||||
UpdateWithdrawApplication(ctx context.Context, applicationID string, data map[string]any) error // 更新提现申请
|
||||
}
|
||||
|
||||
func NewChatDatabase(cli *mongoutil.Client, rdb redis.UniversalClient) (ChatDatabaseInterface, error) {
|
||||
register, err := chat.NewRegister(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account, err := chat.NewAccount(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attribute, err := chat.NewAttribute(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
credential, err := chat.NewCredential(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userLoginRecord, err := chat.NewUserLoginRecord(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verifyCode, err := chat.NewVerifyCode(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
forbiddenAccount, err := admindb.NewForbiddenAccount(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sensitiveWord, err := chat.NewSensitiveWord(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
favorite, err := chat.NewFavorite(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scheduledTask, err := chat.NewScheduledTask(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
systemConfig, err := chat.NewSystemConfig(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wallet, err := chat.NewWallet(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
walletBalanceRecord, err := chat.NewWalletBalanceRecord(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdraw, err := chat.NewWithdraw(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawApplication, err := chat.NewWithdrawApplication(cli.GetDB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var userLoginIPCache cache.UserLoginIPInterface
|
||||
if rdb != nil {
|
||||
userLoginIPCache = cache.NewUserLoginIPInterface(rdb)
|
||||
}
|
||||
|
||||
return &ChatDatabase{
|
||||
tx: cli.GetTx(),
|
||||
register: register,
|
||||
account: account,
|
||||
attribute: attribute,
|
||||
credential: credential,
|
||||
userLoginRecord: userLoginRecord,
|
||||
verifyCode: verifyCode,
|
||||
forbiddenAccount: forbiddenAccount,
|
||||
sensitiveWord: sensitiveWord,
|
||||
favorite: favorite,
|
||||
scheduledTask: scheduledTask,
|
||||
systemConfig: systemConfig,
|
||||
wallet: wallet,
|
||||
walletBalanceRecord: walletBalanceRecord,
|
||||
withdraw: withdraw,
|
||||
withdrawApplication: withdrawApplication,
|
||||
userLoginIPCache: userLoginIPCache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type ChatDatabase struct {
|
||||
tx tx.Tx
|
||||
register chatdb.RegisterInterface
|
||||
account chatdb.AccountInterface
|
||||
attribute chatdb.AttributeInterface
|
||||
credential chatdb.CredentialInterface
|
||||
userLoginRecord chatdb.UserLoginRecordInterface
|
||||
verifyCode chatdb.VerifyCodeInterface
|
||||
forbiddenAccount admin.ForbiddenAccountInterface
|
||||
sensitiveWord chatdb.SensitiveWordInterface
|
||||
favorite chatdb.FavoriteInterface
|
||||
scheduledTask chatdb.ScheduledTaskInterface
|
||||
systemConfig chatdb.SystemConfigInterface
|
||||
wallet chatdb.WalletInterface
|
||||
walletBalanceRecord chatdb.WalletBalanceRecordInterface
|
||||
withdraw chatdb.WithdrawInterface
|
||||
withdrawApplication chatdb.WithdrawApplicationInterface
|
||||
userLoginIPCache cache.UserLoginIPInterface // 用户登录IP缓存(可选,如果为nil则不使用缓存)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetUser(ctx context.Context, userID string) (account *chatdb.Account, err error) {
|
||||
return o.account.Take(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateUseInfo(ctx context.Context, userID string, attribute map[string]any, updateCred, delCred []*chatdb.Credential) (err error) {
|
||||
return o.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err = o.attribute.Update(ctx, userID, attribute); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, credential := range updateCred {
|
||||
if err = o.credential.CreateOrUpdateAccount(ctx, credential); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = o.credential.DeleteByUserIDType(ctx, delCred...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) FindAttribute(ctx context.Context, userIDs []string) ([]*chatdb.Attribute, error) {
|
||||
return o.attribute.Find(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) FindAttributeByAccount(ctx context.Context, accounts []string) ([]*chatdb.Attribute, error) {
|
||||
return o.attribute.FindAccount(ctx, accounts)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) TakeAttributeByPhone(ctx context.Context, areaCode string, phoneNumber string) (*chatdb.Attribute, error) {
|
||||
return o.attribute.TakePhone(ctx, areaCode, phoneNumber)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) TakeAttributeByEmail(ctx context.Context, email string) (*chatdb.Attribute, error) {
|
||||
return o.attribute.TakeEmail(ctx, email)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) TakeAttributeByAccount(ctx context.Context, account string) (*chatdb.Attribute, error) {
|
||||
return o.attribute.TakeAccount(ctx, account)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) TakeAttributeByUserID(ctx context.Context, userID string) (*chatdb.Attribute, error) {
|
||||
return o.attribute.Take(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) TakeLastVerifyCode(ctx context.Context, account string) (*chatdb.VerifyCode, error) {
|
||||
return o.verifyCode.TakeLast(ctx, account)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) TakeAccount(ctx context.Context, userID string) (*chatdb.Account, error) {
|
||||
return o.account.Take(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) TakeCredentialByAccount(ctx context.Context, account string) (*chatdb.Credential, error) {
|
||||
return o.credential.TakeAccount(ctx, account)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) TakeCredentialsByUserID(ctx context.Context, userID string) ([]*chatdb.Credential, error) {
|
||||
return o.credential.Find(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) Search(ctx context.Context, normalUser int32, keyword string, genders int32, startTime, endTime *time.Time, pagination pagination.Pagination) (total int64, attributes []*chatdb.Attribute, err error) {
|
||||
var forbiddenIDs []string
|
||||
if int(normalUser) == constant.NormalUser {
|
||||
forbiddenIDs, err = o.forbiddenAccount.FindAllIDs(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
total, totalUser, err := o.attribute.SearchNormalUser(ctx, keyword, forbiddenIDs, genders, startTime, endTime, pagination)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, totalUser, nil
|
||||
}
|
||||
|
||||
// SearchWithRealNameAuth 搜索用户(支持实名信息搜索)
|
||||
func (o *ChatDatabase) SearchWithRealNameAuth(ctx context.Context, normalUser int32, keyword string, genders int32, startTime, endTime *time.Time, realNameKeyword string, idCardKeyword string, pagination pagination.Pagination) (total int64, attributes []*chatdb.Attribute, err error) {
|
||||
// 如果提供了实名信息搜索关键词,先查询钱包获取userIDs
|
||||
var realNameAuthUserIDs []string
|
||||
if realNameKeyword != "" || idCardKeyword != "" {
|
||||
realNameAuthUserIDs, err = o.wallet.SearchByRealNameAuth(ctx, realNameKeyword, idCardKeyword)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
// 如果没有找到匹配的用户,直接返回空结果
|
||||
if len(realNameAuthUserIDs) == 0 {
|
||||
return 0, []*chatdb.Attribute{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
var forbiddenIDs []string
|
||||
if int(normalUser) == constant.NormalUser {
|
||||
forbiddenIDs, err = o.forbiddenAccount.FindAllIDs(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 如果提供了实名信息搜索,需要在用户ID列表中过滤
|
||||
if len(realNameAuthUserIDs) > 0 {
|
||||
// 修改 SearchNormalUser 方法,支持传入额外的 userIDs 过滤条件
|
||||
// 或者创建一个新的方法
|
||||
// 这里我们修改 SearchNormalUser 来支持这个功能
|
||||
total, totalUser, err := o.attribute.SearchNormalUserWithUserIDs(ctx, keyword, forbiddenIDs, genders, startTime, endTime, realNameAuthUserIDs, pagination)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return total, totalUser, nil
|
||||
}
|
||||
|
||||
// 没有实名信息搜索,使用原来的方法
|
||||
return o.Search(ctx, normalUser, keyword, genders, startTime, endTime, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) SearchUser(ctx context.Context, keyword string, userIDs []string, genders []int32, pagination pagination.Pagination) (int64, []*chatdb.Attribute, error) {
|
||||
return o.attribute.SearchUser(ctx, keyword, userIDs, genders, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) CountVerifyCodeRange(ctx context.Context, account string, start time.Time, end time.Time) (int64, error) {
|
||||
return o.verifyCode.RangeNum(ctx, account, start, end)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) AddVerifyCode(ctx context.Context, verifyCode *chatdb.VerifyCode, fn func() error) error {
|
||||
return o.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err := o.verifyCode.Add(ctx, []*chatdb.VerifyCode{verifyCode}); err != nil {
|
||||
return err
|
||||
}
|
||||
if fn != nil {
|
||||
return fn()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateVerifyCodeIncrCount(ctx context.Context, id string) error {
|
||||
return o.verifyCode.Incr(ctx, id)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DelVerifyCode(ctx context.Context, id string) error {
|
||||
return o.verifyCode.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) RegisterUser(ctx context.Context, register *chatdb.Register, account *chatdb.Account, attribute *chatdb.Attribute, credentials []*chatdb.Credential) error {
|
||||
return o.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err := o.register.Create(ctx, register); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.account.Create(ctx, account); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.attribute.Create(ctx, attribute); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.credential.Create(ctx, credentials...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) LoginRecord(ctx context.Context, record *chatdb.UserLoginRecord, verifyCodeID *string) error {
|
||||
return o.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err := o.userLoginRecord.Create(ctx, record); err != nil {
|
||||
return err
|
||||
}
|
||||
// 创建登录记录后,更新缓存(保证缓存一致性)
|
||||
if o.userLoginIPCache != nil && record.UserID != "" {
|
||||
// 先删除旧缓存,然后设置新缓存
|
||||
// 使用新IP更新缓存,这样下次查询时可以直接从缓存获取
|
||||
_ = o.userLoginIPCache.SetLatestLoginIP(ctx, record.UserID, record.IP)
|
||||
}
|
||||
if verifyCodeID != nil {
|
||||
if err := o.verifyCode.Delete(ctx, *verifyCodeID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdatePassword(ctx context.Context, userID string, password string) error {
|
||||
return o.account.UpdatePassword(ctx, userID, password)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdatePasswordAndDeleteVerifyCode(ctx context.Context, userID string, password string, codeID string) error {
|
||||
return o.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err := o.account.UpdatePassword(ctx, userID, password); err != nil {
|
||||
return err
|
||||
}
|
||||
if codeID == "" {
|
||||
return nil
|
||||
}
|
||||
if err := o.verifyCode.Delete(ctx, codeID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) NewUserCountTotal(ctx context.Context, before *time.Time) (int64, error) {
|
||||
return o.register.CountTotal(ctx, before)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UserLoginCountTotal(ctx context.Context, before *time.Time) (int64, error) {
|
||||
return o.userLoginRecord.CountTotal(ctx, before)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UserLoginCountRangeEverydayTotal(ctx context.Context, start *time.Time, end *time.Time) (map[string]int64, int64, error) {
|
||||
return o.userLoginRecord.CountRangeEverydayTotal(ctx, start, end)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) CountTodayRegisteredUsers(ctx context.Context) (int64, error) {
|
||||
return o.register.CountToday(ctx)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) CountTodayActiveUsers(ctx context.Context) (int64, error) {
|
||||
return o.userLoginRecord.CountTodayActiveUsers(ctx)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetLatestLoginIP(ctx context.Context, userID string) (string, error) {
|
||||
// 如果启用了缓存,先尝试从缓存获取
|
||||
if o.userLoginIPCache != nil {
|
||||
ip, found, err := o.userLoginIPCache.GetLatestLoginIP(ctx, userID)
|
||||
if err != nil {
|
||||
// 缓存查询出错,降级到数据库查询
|
||||
return o.userLoginRecord.GetLatestLoginIP(ctx, userID)
|
||||
}
|
||||
// 如果缓存命中,直接返回(即使IP为空字符串,也表示用户确实没有IP)
|
||||
if found {
|
||||
return ip, nil
|
||||
}
|
||||
// 缓存未命中,从数据库查询
|
||||
ip, err = o.userLoginRecord.GetLatestLoginIP(ctx, userID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 将查询结果写入缓存(即使为空也缓存,避免频繁查询数据库)
|
||||
_ = o.userLoginIPCache.SetLatestLoginIP(ctx, userID, ip)
|
||||
return ip, nil
|
||||
}
|
||||
// 未启用缓存,直接从数据库查询
|
||||
return o.userLoginRecord.GetLatestLoginIP(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) SearchUserLoginRecords(ctx context.Context, userID, ip string, pagination pagination.Pagination) (int64, []*chatdb.UserLoginRecord, error) {
|
||||
return o.userLoginRecord.Search(ctx, userID, ip, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DelUserAccount(ctx context.Context, userIDs []string) error {
|
||||
return o.tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
if err := o.register.Delete(ctx, userIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.account.Delete(ctx, userIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.attribute.Delete(ctx, userIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== 敏感词相关方法实现 ====================
|
||||
|
||||
// GetSensitiveWords 获取所有启用的敏感词
|
||||
func (o *ChatDatabase) GetSensitiveWords(ctx context.Context) ([]*chatdb.SensitiveWord, error) {
|
||||
return o.sensitiveWord.GetEnabledSensitiveWords(ctx)
|
||||
}
|
||||
|
||||
// CheckSensitiveWords 检测敏感词
|
||||
func (o *ChatDatabase) CheckSensitiveWords(ctx context.Context, content string) ([]*chatdb.SensitiveWord, bool, error) {
|
||||
words, err := o.sensitiveWord.CheckSensitiveWords(ctx, content)
|
||||
hasSensitive := len(words) > 0
|
||||
return words, hasSensitive, err
|
||||
}
|
||||
|
||||
// FilterContent 过滤内容
|
||||
func (o *ChatDatabase) FilterContent(ctx context.Context, content string) (string, []*chatdb.SensitiveWord, bool, error) {
|
||||
filteredContent, words, err := o.sensitiveWord.FilterContent(ctx, content)
|
||||
hasSensitive := len(words) > 0
|
||||
return filteredContent, words, hasSensitive, err
|
||||
}
|
||||
|
||||
// GetSensitiveWordConfig 获取敏感词配置
|
||||
func (o *ChatDatabase) GetSensitiveWordConfig(ctx context.Context) (*chatdb.SensitiveWordConfig, error) {
|
||||
return o.sensitiveWord.GetSensitiveWordConfig(ctx)
|
||||
}
|
||||
|
||||
// ==================== 敏感词管理相关方法实现 ====================
|
||||
|
||||
func (o *ChatDatabase) CreateSensitiveWord(ctx context.Context, word *chatdb.SensitiveWord) error {
|
||||
return o.sensitiveWord.CreateSensitiveWord(ctx, word)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateSensitiveWord(ctx context.Context, id string, data map[string]any) error {
|
||||
return o.sensitiveWord.UpdateSensitiveWord(ctx, id, data)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DeleteSensitiveWord(ctx context.Context, ids []string) error {
|
||||
return o.sensitiveWord.DeleteSensitiveWord(ctx, ids)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetSensitiveWord(ctx context.Context, id string) (*chatdb.SensitiveWord, error) {
|
||||
return o.sensitiveWord.GetSensitiveWord(ctx, id)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) SearchSensitiveWords(ctx context.Context, keyword string, action int32, status int32, pagination pagination.Pagination) (int64, []*chatdb.SensitiveWord, error) {
|
||||
return o.sensitiveWord.SearchSensitiveWords(ctx, keyword, action, status, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) BatchAddSensitiveWords(ctx context.Context, words []*chatdb.SensitiveWord) error {
|
||||
return o.sensitiveWord.BatchCreateSensitiveWords(ctx, words)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) BatchUpdateSensitiveWords(ctx context.Context, updates map[string]map[string]any) error {
|
||||
return o.sensitiveWord.BatchUpdateSensitiveWords(ctx, updates)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) BatchDeleteSensitiveWords(ctx context.Context, ids []string) error {
|
||||
return o.sensitiveWord.BatchDeleteSensitiveWords(ctx, ids)
|
||||
}
|
||||
|
||||
// ==================== 敏感词分组管理实现 ====================
|
||||
|
||||
func (o *ChatDatabase) CreateSensitiveWordGroup(ctx context.Context, group *chatdb.SensitiveWordGroup) error {
|
||||
return o.sensitiveWord.CreateSensitiveWordGroup(ctx, group)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateSensitiveWordGroup(ctx context.Context, id string, data map[string]any) error {
|
||||
return o.sensitiveWord.UpdateSensitiveWordGroup(ctx, id, data)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DeleteSensitiveWordGroup(ctx context.Context, ids []string) error {
|
||||
return o.sensitiveWord.DeleteSensitiveWordGroup(ctx, ids)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetSensitiveWordGroup(ctx context.Context, id string) (*chatdb.SensitiveWordGroup, error) {
|
||||
return o.sensitiveWord.GetSensitiveWordGroup(ctx, id)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetAllSensitiveWordGroups(ctx context.Context) ([]*chatdb.SensitiveWordGroup, error) {
|
||||
return o.sensitiveWord.GetAllSensitiveWordGroups(ctx)
|
||||
}
|
||||
|
||||
// ==================== 敏感词配置管理实现 ====================
|
||||
|
||||
func (o *ChatDatabase) UpdateSensitiveWordConfig(ctx context.Context, config *chatdb.SensitiveWordConfig) error {
|
||||
return o.sensitiveWord.UpdateSensitiveWordConfig(ctx, config)
|
||||
}
|
||||
|
||||
// ==================== 敏感词日志管理实现 ====================
|
||||
|
||||
func (o *ChatDatabase) GetSensitiveWordLogs(ctx context.Context, userID, groupID string, pagination pagination.Pagination) (int64, []*chatdb.SensitiveWordLog, error) {
|
||||
return o.sensitiveWord.GetSensitiveWordLogs(ctx, userID, groupID, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DeleteSensitiveWordLogs(ctx context.Context, ids []string) error {
|
||||
return o.sensitiveWord.DeleteSensitiveWordLogs(ctx, ids)
|
||||
}
|
||||
|
||||
// ==================== 敏感词统计实现 ====================
|
||||
|
||||
func (o *ChatDatabase) GetSensitiveWordStats(ctx context.Context) (map[string]int64, error) {
|
||||
return o.sensitiveWord.GetSensitiveWordStats(ctx)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetSensitiveWordLogStats(ctx context.Context, startTime, endTime time.Time) (map[string]int64, error) {
|
||||
return o.sensitiveWord.GetSensitiveWordLogStats(ctx, startTime, endTime)
|
||||
}
|
||||
|
||||
// ==================== 收藏相关方法实现 ====================
|
||||
|
||||
func (o *ChatDatabase) CreateFavorite(ctx context.Context, favorite *chatdb.Favorite) error {
|
||||
return o.favorite.Create(ctx, favorite)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetFavorite(ctx context.Context, favoriteID string) (*chatdb.Favorite, error) {
|
||||
return o.favorite.Take(ctx, favoriteID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetFavoritesByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.Favorite, error) {
|
||||
return o.favorite.FindByUserID(ctx, userID, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetFavoritesByUserIDAndType(ctx context.Context, userID string, favoriteType int32, pagination pagination.Pagination) (int64, []*chatdb.Favorite, error) {
|
||||
return o.favorite.FindByUserIDAndType(ctx, userID, favoriteType, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) SearchFavoritesByKeyword(ctx context.Context, userID string, keyword string, pagination pagination.Pagination) (int64, []*chatdb.Favorite, error) {
|
||||
return o.favorite.SearchByKeyword(ctx, userID, keyword, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateFavorite(ctx context.Context, favoriteID string, data map[string]any) error {
|
||||
return o.favorite.Update(ctx, favoriteID, data)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DeleteFavorite(ctx context.Context, favoriteIDs []string) error {
|
||||
return o.favorite.Delete(ctx, favoriteIDs)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DeleteFavoritesByUserID(ctx context.Context, userID string) error {
|
||||
return o.favorite.DeleteByUserID(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) CountFavoritesByUserID(ctx context.Context, userID string) (int64, error) {
|
||||
return o.favorite.CountByUserID(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetFavoritesByTags(ctx context.Context, userID string, tags []string, pagination pagination.Pagination) (int64, []*chatdb.Favorite, error) {
|
||||
return o.favorite.FindByTags(ctx, userID, tags, pagination)
|
||||
}
|
||||
|
||||
// ==================== 定时任务相关方法实现 ====================
|
||||
|
||||
func (o *ChatDatabase) CreateScheduledTask(ctx context.Context, task *chatdb.ScheduledTask) error {
|
||||
return o.scheduledTask.Create(ctx, task)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetScheduledTask(ctx context.Context, taskID string) (*chatdb.ScheduledTask, error) {
|
||||
return o.scheduledTask.Take(ctx, taskID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetScheduledTasksByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.ScheduledTask, error) {
|
||||
return o.scheduledTask.FindByUserID(ctx, userID, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetAllScheduledTasks(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.ScheduledTask, error) {
|
||||
return o.scheduledTask.FindAll(ctx, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateScheduledTask(ctx context.Context, taskID string, data map[string]any) error {
|
||||
return o.scheduledTask.Update(ctx, taskID, data)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DeleteScheduledTask(ctx context.Context, taskIDs []string) error {
|
||||
return o.scheduledTask.Delete(ctx, taskIDs)
|
||||
}
|
||||
|
||||
// ==================== 系统配置相关方法实现 ====================
|
||||
|
||||
func (o *ChatDatabase) CreateSystemConfig(ctx context.Context, config *chatdb.SystemConfig) error {
|
||||
return o.systemConfig.Create(ctx, config)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetSystemConfig(ctx context.Context, key string) (*chatdb.SystemConfig, error) {
|
||||
return o.systemConfig.Take(ctx, key)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetSystemConfigsByKeys(ctx context.Context, keys []string) ([]*chatdb.SystemConfig, error) {
|
||||
return o.systemConfig.FindByKeys(ctx, keys)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetAllSystemConfigs(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.SystemConfig, error) {
|
||||
return o.systemConfig.FindAll(ctx, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateSystemConfig(ctx context.Context, key string, data map[string]any) error {
|
||||
return o.systemConfig.Update(ctx, key, data)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateSystemConfigValue(ctx context.Context, key string, value string) error {
|
||||
return o.systemConfig.UpdateValue(ctx, key, value)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateSystemConfigEnabled(ctx context.Context, key string, enabled bool) error {
|
||||
return o.systemConfig.UpdateEnabled(ctx, key, enabled)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) DeleteSystemConfig(ctx context.Context, keys []string) error {
|
||||
return o.systemConfig.Delete(ctx, keys)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetEnabledSystemConfigs(ctx context.Context) ([]*chatdb.SystemConfig, error) {
|
||||
return o.systemConfig.GetEnabledConfigs(ctx)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetAppSystemConfigs(ctx context.Context) ([]*chatdb.SystemConfig, error) {
|
||||
return o.systemConfig.GetAppConfigs(ctx)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWallet(ctx context.Context, userID string) (*chatdb.Wallet, error) {
|
||||
return o.wallet.Take(ctx, userID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWalletsByUserIDs(ctx context.Context, userIDs []string) ([]*chatdb.Wallet, error) {
|
||||
return o.wallet.Find(ctx, userIDs)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWalletsPage(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.Wallet, error) {
|
||||
return o.wallet.Page(ctx, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWalletsPageByRealNameAuthAuditStatus(ctx context.Context, auditStatus int32, userID string, pagination pagination.Pagination) (int64, []*chatdb.Wallet, error) {
|
||||
return o.wallet.PageByRealNameAuthAuditStatus(ctx, auditStatus, userID, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateWalletBalance(ctx context.Context, userID string, balance int64) error {
|
||||
return o.wallet.UpdateBalance(ctx, userID, balance)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) IncrementWalletBalance(ctx context.Context, userID string, amount int64) (beforeBalance int64, afterBalance int64, err error) {
|
||||
return o.wallet.IncrementBalance(ctx, userID, amount)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateWalletPaymentPassword(ctx context.Context, userID string, paymentPassword string) error {
|
||||
return o.wallet.UpdatePaymentPassword(ctx, userID, paymentPassword)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateWalletWithdrawAccount(ctx context.Context, userID string, withdrawAccount string) error {
|
||||
return o.wallet.UpdateWithdrawAccount(ctx, userID, withdrawAccount)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateWalletWithdrawAccountWithType(ctx context.Context, userID string, withdrawAccount string, accountType int32) error {
|
||||
return o.wallet.UpdateWithdrawAccountWithType(ctx, userID, withdrawAccount, accountType)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateWalletRealNameAuth(ctx context.Context, userID string, realNameAuth chatdb.RealNameAuth) error {
|
||||
return o.wallet.UpdateRealNameAuth(ctx, userID, realNameAuth)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) CreateWallet(ctx context.Context, wallet *chatdb.Wallet) error {
|
||||
return o.wallet.Create(ctx, wallet)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWalletBalanceRecords(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.WalletBalanceRecord, error) {
|
||||
return o.walletBalanceRecord.FindByUserID(ctx, userID, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWalletBalanceRecordsByUserIDAndType(ctx context.Context, userID string, recordType int32, pagination pagination.Pagination) (int64, []*chatdb.WalletBalanceRecord, error) {
|
||||
return o.walletBalanceRecord.FindByUserIDAndType(ctx, userID, recordType, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) CreateWalletBalanceRecord(ctx context.Context, record *chatdb.WalletBalanceRecord) error {
|
||||
return o.walletBalanceRecord.Create(ctx, record)
|
||||
}
|
||||
|
||||
// ==================== 提现相关方法 ====================
|
||||
|
||||
func (o *ChatDatabase) CreateWithdraw(ctx context.Context, withdraw *chatdb.Withdraw) error {
|
||||
return o.withdraw.Create(ctx, withdraw)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWithdraw(ctx context.Context, withdrawID string) (*chatdb.Withdraw, error) {
|
||||
return o.withdraw.Take(ctx, withdrawID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWithdrawsByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) {
|
||||
return o.withdraw.FindByUserID(ctx, userID, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWithdrawsByStatus(ctx context.Context, status int32, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) {
|
||||
return o.withdraw.FindByStatus(ctx, status, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateWithdrawStatus(ctx context.Context, withdrawID string, status int32, auditorID string, auditRemark string) error {
|
||||
return o.withdraw.UpdateStatus(ctx, withdrawID, status, auditorID, auditRemark)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWithdrawsPage(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) {
|
||||
return o.withdraw.Page(ctx, pagination)
|
||||
}
|
||||
|
||||
// ==================== 提现申请相关方法 ====================
|
||||
|
||||
func (o *ChatDatabase) CreateWithdrawApplication(ctx context.Context, application *chatdb.WithdrawApplication) error {
|
||||
return o.withdrawApplication.Create(ctx, application)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWithdrawApplication(ctx context.Context, applicationID string) (*chatdb.WithdrawApplication, error) {
|
||||
return o.withdrawApplication.Take(ctx, applicationID)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWithdrawApplicationsByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) {
|
||||
return o.withdrawApplication.FindByUserID(ctx, userID, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWithdrawApplicationsByStatus(ctx context.Context, status int32, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) {
|
||||
return o.withdrawApplication.FindByStatus(ctx, status, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateWithdrawApplicationStatus(ctx context.Context, applicationID string, status int32, auditorID string, auditRemark string) error {
|
||||
return o.withdrawApplication.UpdateStatus(ctx, applicationID, status, auditorID, auditRemark)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) GetWithdrawApplicationsPage(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) {
|
||||
return o.withdrawApplication.Page(ctx, pagination)
|
||||
}
|
||||
|
||||
func (o *ChatDatabase) UpdateWithdrawApplication(ctx context.Context, applicationID string, data map[string]any) error {
|
||||
return o.withdrawApplication.Update(ctx, applicationID, data)
|
||||
}
|
||||
24
pkg/common/db/dbutil/gorm.go
Normal file
24
pkg/common/db/dbutil/gorm.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 dbutil
|
||||
|
||||
import (
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
func IsDBNotFound(err error) bool {
|
||||
return errs.Unwrap(err) == mongo.ErrNoDocuments
|
||||
}
|
||||
103
pkg/common/db/model/admin/admin.go
Normal file
103
pkg/common/db/model/admin/admin.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
admindb "git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func NewAdmin(db *mongo.Database) (admindb.AdminInterface, error) {
|
||||
coll := db.Collection("admin")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "account", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Admin{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Admin struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Admin) Take(ctx context.Context, account string) (*admindb.Admin, error) {
|
||||
return mongoutil.FindOne[*admindb.Admin](ctx, o.coll, bson.M{"account": account})
|
||||
}
|
||||
|
||||
func (o *Admin) TakeUserID(ctx context.Context, userID string) (*admindb.Admin, error) {
|
||||
return mongoutil.FindOne[*admindb.Admin](ctx, o.coll, bson.M{"user_id": userID})
|
||||
}
|
||||
|
||||
func (o *Admin) Update(ctx context.Context, account string, update map[string]any) error {
|
||||
if len(update) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": account}, bson.M{"$set": update}, false)
|
||||
}
|
||||
|
||||
func (o *Admin) Create(ctx context.Context, admins []*admindb.Admin) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, admins)
|
||||
}
|
||||
|
||||
func (o *Admin) ChangePassword(ctx context.Context, userID string, newPassword string) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": bson.M{"password": newPassword}}, false)
|
||||
}
|
||||
|
||||
func (o *Admin) ChangeOperationPassword(ctx context.Context, userID string, newPassword string) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": bson.M{"operation_password": newPassword}}, false)
|
||||
}
|
||||
|
||||
func (o *Admin) ClearGoogleAuthKey(ctx context.Context, userID string) error {
|
||||
// 使用 $unset 删除字段,确保字段被完全移除
|
||||
// 注意:$unset 操作符的值可以是任意值(通常使用空字符串或1),MongoDB 会忽略值,只删除字段
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$unset": bson.M{"google_auth_key": 1}}, false)
|
||||
}
|
||||
|
||||
func (o *Admin) Delete(ctx context.Context, userIDs []string) error {
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *Admin) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admindb.Admin, error) {
|
||||
opt := options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}})
|
||||
filter := bson.M{}
|
||||
|
||||
// 如果有关键词,则进行模糊搜索(账号、昵称、用户ID)
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"account": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"nickname": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
|
||||
return mongoutil.FindPage[*admindb.Admin](ctx, o.coll, filter, pagination, opt)
|
||||
}
|
||||
95
pkg/common/db/model/admin/applet.go
Normal file
95
pkg/common/db/model/admin/applet.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewApplet(db *mongo.Database) (admin.AppletInterface, error) {
|
||||
coll := db.Collection("applet")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Applet{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Applet struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Applet) Create(ctx context.Context, applets []*admin.Applet) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, applets)
|
||||
}
|
||||
|
||||
func (o *Applet) Del(ctx context.Context, ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
func (o *Applet) Update(ctx context.Context, id string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"id": id}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *Applet) Take(ctx context.Context, id string) (*admin.Applet, error) {
|
||||
return mongoutil.FindOne[*admin.Applet](ctx, o.coll, bson.M{"id": id})
|
||||
}
|
||||
|
||||
func (o *Applet) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admin.Applet, error) {
|
||||
filter := bson.M{}
|
||||
|
||||
if keyword != "" {
|
||||
filter = bson.M{
|
||||
"$or": []bson.M{
|
||||
{"name": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"app_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"version": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
},
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*admin.Applet](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *Applet) FindOnShelf(ctx context.Context) ([]*admin.Applet, error) {
|
||||
return mongoutil.Find[*admin.Applet](ctx, o.coll, bson.M{"status": constant.StatusOnShelf})
|
||||
}
|
||||
|
||||
func (o *Applet) FindID(ctx context.Context, ids []string) ([]*admin.Applet, error) {
|
||||
return mongoutil.Find[*admin.Applet](ctx, o.coll, bson.M{"id": bson.M{"$in": ids}})
|
||||
}
|
||||
84
pkg/common/db/model/admin/application.go
Normal file
84
pkg/common/db/model/admin/application.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
admindb "git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewApplication(db *mongo.Database) (admindb.ApplicationInterface, error) {
|
||||
coll := db.Collection("application")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "platform", Value: 1},
|
||||
{Key: "version", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "latest", Value: -1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ApplicationMgo{coll: coll}, nil
|
||||
}
|
||||
|
||||
type ApplicationMgo struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (a *ApplicationMgo) sort() any {
|
||||
return bson.D{{"latest", -1}, {"_id", -1}}
|
||||
}
|
||||
|
||||
func (a *ApplicationMgo) LatestVersion(ctx context.Context, platform string) (*admin.Application, error) {
|
||||
return mongoutil.FindOne[*admin.Application](ctx, a.coll, bson.M{"platform": platform}, options.FindOne().SetSort(a.sort()))
|
||||
}
|
||||
|
||||
func (a *ApplicationMgo) AddVersion(ctx context.Context, val *admin.Application) error {
|
||||
if val.ID.IsZero() {
|
||||
val.ID = primitive.NewObjectID()
|
||||
}
|
||||
return mongoutil.InsertMany(ctx, a.coll, []*admin.Application{val})
|
||||
}
|
||||
|
||||
func (a *ApplicationMgo) UpdateVersion(ctx context.Context, id primitive.ObjectID, update map[string]any) error {
|
||||
if len(update) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, a.coll, bson.M{"_id": id}, bson.M{"$set": update}, true)
|
||||
}
|
||||
|
||||
func (a *ApplicationMgo) DeleteVersion(ctx context.Context, id []primitive.ObjectID) error {
|
||||
if len(id) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, a.coll, bson.M{"_id": bson.M{"$in": id}})
|
||||
}
|
||||
|
||||
func (a *ApplicationMgo) PageVersion(ctx context.Context, platforms []string, page pagination.Pagination) (int64, []*admin.Application, error) {
|
||||
filter := bson.M{}
|
||||
if len(platforms) > 0 {
|
||||
filter["platform"] = bson.M{"$in": platforms}
|
||||
}
|
||||
return mongoutil.FindPage[*admin.Application](ctx, a.coll, filter, page, options.Find().SetSort(a.sort()))
|
||||
}
|
||||
|
||||
func (a *ApplicationMgo) FindPlatform(ctx context.Context, id []primitive.ObjectID) ([]string, error) {
|
||||
if len(id) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return mongoutil.Find[string](ctx, a.coll, bson.M{"_id": bson.M{"$in": id}}, options.Find().SetProjection(bson.M{"_id": 0, "platform": 1}))
|
||||
}
|
||||
77
pkg/common/db/model/admin/client_config.go
Normal file
77
pkg/common/db/model/admin/client_config.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewClientConfig(db *mongo.Database) (admin.ClientConfigInterface, error) {
|
||||
coll := db.Collection("client_config")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "key", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &ClientConfig{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type ClientConfig struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *ClientConfig) Set(ctx context.Context, config map[string]string) error {
|
||||
for key, value := range config {
|
||||
filter := bson.M{"key": key}
|
||||
update := bson.M{
|
||||
"value": value,
|
||||
}
|
||||
err := mongoutil.UpdateOne(ctx, o.coll, filter, bson.M{"$set": update}, false, options.Update().SetUpsert(true))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ClientConfig) Del(ctx context.Context, keys []string) error {
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"key": bson.M{"$in": keys}})
|
||||
}
|
||||
|
||||
func (o *ClientConfig) Get(ctx context.Context) (map[string]string, error) {
|
||||
cs, err := mongoutil.Find[*admin.ClientConfig](ctx, o.coll, bson.M{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cm := make(map[string]string)
|
||||
for _, config := range cs {
|
||||
cm[config.Key] = config.Value
|
||||
}
|
||||
return cm, nil
|
||||
}
|
||||
86
pkg/common/db/model/admin/forbidden_account.go
Normal file
86
pkg/common/db/model/admin/forbidden_account.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewForbiddenAccount(db *mongo.Database) (admin.ForbiddenAccountInterface, error) {
|
||||
coll := db.Collection("forbidden_account")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &ForbiddenAccount{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type ForbiddenAccount struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *ForbiddenAccount) Create(ctx context.Context, ms []*admin.ForbiddenAccount) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, ms)
|
||||
}
|
||||
|
||||
func (o *ForbiddenAccount) Take(ctx context.Context, userID string) (*admin.ForbiddenAccount, error) {
|
||||
return mongoutil.FindOne[*admin.ForbiddenAccount](ctx, o.coll, bson.M{"user_id": userID})
|
||||
}
|
||||
|
||||
func (o *ForbiddenAccount) Delete(ctx context.Context, userIDs []string) error {
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *ForbiddenAccount) Find(ctx context.Context, userIDs []string) ([]*admin.ForbiddenAccount, error) {
|
||||
return mongoutil.Find[*admin.ForbiddenAccount](ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *ForbiddenAccount) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admin.ForbiddenAccount, error) {
|
||||
filter := bson.M{}
|
||||
|
||||
if keyword != "" {
|
||||
filter = bson.M{
|
||||
"$or": []bson.M{
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"reason": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"operator_user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
},
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*admin.ForbiddenAccount](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *ForbiddenAccount) FindAllIDs(ctx context.Context) ([]string, error) {
|
||||
return mongoutil.Find[string](ctx, o.coll, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
|
||||
}
|
||||
99
pkg/common/db/model/admin/invitation_register.go
Normal file
99
pkg/common/db/model/admin/invitation_register.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
admindb "git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewInvitationRegister(db *mongo.Database) (admindb.InvitationRegisterInterface, error) {
|
||||
coll := db.Collection("invitation_register")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "invitation_code", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &InvitationRegister{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type InvitationRegister struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *InvitationRegister) Find(ctx context.Context, codes []string) ([]*admindb.InvitationRegister, error) {
|
||||
return mongoutil.Find[*admindb.InvitationRegister](ctx, o.coll, bson.M{"invitation_code": bson.M{"$in": codes}})
|
||||
}
|
||||
|
||||
func (o *InvitationRegister) Del(ctx context.Context, codes []string) error {
|
||||
if len(codes) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"invitation_code": bson.M{"$in": codes}})
|
||||
}
|
||||
|
||||
func (o *InvitationRegister) Create(ctx context.Context, v []*admindb.InvitationRegister) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, v)
|
||||
}
|
||||
|
||||
func (o *InvitationRegister) Take(ctx context.Context, code string) (*admindb.InvitationRegister, error) {
|
||||
return mongoutil.FindOne[*admindb.InvitationRegister](ctx, o.coll, bson.M{"code": code})
|
||||
}
|
||||
|
||||
func (o *InvitationRegister) Update(ctx context.Context, code string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"invitation_code": code}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *InvitationRegister) Search(ctx context.Context, keyword string, state int32, userIDs []string, codes []string, pagination pagination.Pagination) (int64, []*admindb.InvitationRegister, error) {
|
||||
filter := bson.M{}
|
||||
switch state {
|
||||
case constant.InvitationCodeUsed:
|
||||
filter = bson.M{"user_id": bson.M{"$ne": ""}}
|
||||
case constant.InvitationCodeUnused:
|
||||
filter = bson.M{"user_id": ""}
|
||||
}
|
||||
|
||||
if len(userIDs) > 0 {
|
||||
filter["user_id"] = bson.M{"$in": userIDs}
|
||||
}
|
||||
if len(codes) > 0 {
|
||||
filter["invitation_code"] = bson.M{"$in": codes}
|
||||
}
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"invitation_code": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*admindb.InvitationRegister](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
95
pkg/common/db/model/admin/ip_forbidden.go
Normal file
95
pkg/common/db/model/admin/ip_forbidden.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
admindb "git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewIPForbidden(db *mongo.Database) (admindb.IPForbiddenInterface, error) {
|
||||
coll := db.Collection("ip_forbidden")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "ip", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &IPForbidden{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type IPForbidden struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *IPForbidden) Take(ctx context.Context, ip string) (*admindb.IPForbidden, error) {
|
||||
return mongoutil.FindOne[*admindb.IPForbidden](ctx, o.coll, bson.M{"ip": ip})
|
||||
}
|
||||
|
||||
func (o *IPForbidden) Find(ctx context.Context, ips []string) ([]*admindb.IPForbidden, error) {
|
||||
return mongoutil.Find[*admindb.IPForbidden](ctx, o.coll, bson.M{"ip": bson.M{"$in": ips}})
|
||||
}
|
||||
|
||||
func (o *IPForbidden) Search(ctx context.Context, keyword string, state int32, pagination pagination.Pagination) (int64, []*admindb.IPForbidden, error) {
|
||||
filter := bson.M{}
|
||||
|
||||
switch state {
|
||||
case constant.LimitNil:
|
||||
case constant.LimitEmpty:
|
||||
filter = bson.M{"limit_register": 0, "limit_login": 0}
|
||||
case constant.LimitOnlyRegisterIP:
|
||||
filter = bson.M{"limit_register": 1, "limit_login": 0}
|
||||
case constant.LimitOnlyLoginIP:
|
||||
filter = bson.M{"limit_register": 0, "limit_login": 1}
|
||||
case constant.LimitRegisterIP:
|
||||
filter = bson.M{"limit_register": 1}
|
||||
case constant.LimitLoginIP:
|
||||
filter = bson.M{"limit_login": 1}
|
||||
case constant.LimitLoginRegisterIP:
|
||||
filter = bson.M{"limit_register": 1, "limit_login": 1}
|
||||
}
|
||||
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"ip": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*admindb.IPForbidden](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *IPForbidden) Create(ctx context.Context, ms []*admindb.IPForbidden) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, ms)
|
||||
}
|
||||
|
||||
func (o *IPForbidden) Delete(ctx context.Context, ips []string) error {
|
||||
if len(ips) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"ip": bson.M{"$in": ips}})
|
||||
}
|
||||
93
pkg/common/db/model/admin/limit_user_login_ip.go
Normal file
93
pkg/common/db/model/admin/limit_user_login_ip.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewLimitUserLoginIP(db *mongo.Database) (admin.LimitUserLoginIPInterface, error) {
|
||||
coll := db.Collection("limit_user_login_ip")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "ip", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &LimitUserLoginIP{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type LimitUserLoginIP struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *LimitUserLoginIP) Create(ctx context.Context, ms []*admin.LimitUserLoginIP) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, ms)
|
||||
}
|
||||
|
||||
func (o *LimitUserLoginIP) Delete(ctx context.Context, ms []*admin.LimitUserLoginIP) error {
|
||||
return mongoutil.DeleteMany(ctx, o.coll, o.limitUserLoginIPFilter(ms))
|
||||
}
|
||||
|
||||
func (o *LimitUserLoginIP) Count(ctx context.Context, userID string) (uint32, error) {
|
||||
count, err := mongoutil.Count(ctx, o.coll, bson.M{"user_id": userID})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(count), nil
|
||||
}
|
||||
|
||||
func (o *LimitUserLoginIP) Take(ctx context.Context, userID string, ip string) (*admin.LimitUserLoginIP, error) {
|
||||
return mongoutil.FindOne[*admin.LimitUserLoginIP](ctx, o.coll, bson.M{"user_id": userID, "ip": ip})
|
||||
}
|
||||
|
||||
func (o *LimitUserLoginIP) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admin.LimitUserLoginIP, error) {
|
||||
filter := bson.M{
|
||||
"$or": []bson.M{
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"ip": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
},
|
||||
}
|
||||
return mongoutil.FindPage[*admin.LimitUserLoginIP](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *LimitUserLoginIP) limitUserLoginIPFilter(ips []*admin.LimitUserLoginIP) bson.M {
|
||||
if len(ips) == 0 {
|
||||
return nil
|
||||
}
|
||||
or := make(bson.A, 0, len(ips))
|
||||
for _, ip := range ips {
|
||||
or = append(or, bson.M{
|
||||
"user_id": ip.UserID,
|
||||
"ip": ip.IP,
|
||||
})
|
||||
}
|
||||
return bson.M{"$or": or}
|
||||
}
|
||||
76
pkg/common/db/model/admin/register_add_friend.go
Normal file
76
pkg/common/db/model/admin/register_add_friend.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewRegisterAddFriend(db *mongo.Database) (admin.RegisterAddFriendInterface, error) {
|
||||
coll := db.Collection("register_add_friend")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &RegisterAddFriend{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type RegisterAddFriend struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *RegisterAddFriend) Add(ctx context.Context, registerAddFriends []*admin.RegisterAddFriend) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, registerAddFriends)
|
||||
}
|
||||
|
||||
func (o *RegisterAddFriend) Del(ctx context.Context, userIDs []string) error {
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *RegisterAddFriend) FindUserID(ctx context.Context, userIDs []string) ([]string, error) {
|
||||
filter := bson.M{}
|
||||
if len(userIDs) > 0 {
|
||||
filter["user_id"] = bson.M{"$in": userIDs}
|
||||
}
|
||||
return mongoutil.Find[string](ctx, o.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
|
||||
}
|
||||
|
||||
func (o *RegisterAddFriend) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admin.RegisterAddFriend, error) {
|
||||
filter := bson.M{"user_id": bson.M{"$regex": keyword, "$options": "i"}}
|
||||
return mongoutil.FindPage[*admin.RegisterAddFriend](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *RegisterAddFriend) CountTotal(ctx context.Context) (int64, error) {
|
||||
return mongoutil.Count(ctx, o.coll, bson.M{})
|
||||
}
|
||||
90
pkg/common/db/model/admin/register_add_group.go
Normal file
90
pkg/common/db/model/admin/register_add_group.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewRegisterAddGroup(db *mongo.Database) (admin.RegisterAddGroupInterface, error) {
|
||||
coll := db.Collection("register_add_group")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "group_id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &RegisterAddGroup{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type RegisterAddGroup struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *RegisterAddGroup) Add(ctx context.Context, registerAddGroups []*admin.RegisterAddGroup) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, registerAddGroups)
|
||||
}
|
||||
|
||||
func (o *RegisterAddGroup) Del(ctx context.Context, groupIDs []string) error {
|
||||
if len(groupIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"group_id": bson.M{"$in": groupIDs}})
|
||||
}
|
||||
|
||||
func (o *RegisterAddGroup) FindGroupID(ctx context.Context, groupIDs []string) ([]string, error) {
|
||||
filter := bson.M{}
|
||||
if len(groupIDs) > 0 {
|
||||
filter["group_id"] = bson.M{"$in": groupIDs}
|
||||
}
|
||||
return mongoutil.Find[string](ctx, o.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "group_id": 1}))
|
||||
}
|
||||
|
||||
func (o *RegisterAddGroup) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*admin.RegisterAddGroup, error) {
|
||||
filter := bson.M{"group_id": bson.M{"$regex": keyword, "$options": "i"}}
|
||||
return mongoutil.FindPage[*admin.RegisterAddGroup](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *RegisterAddGroup) CountTotal(ctx context.Context) (int64, error) {
|
||||
return mongoutil.Count(ctx, o.coll, bson.M{})
|
||||
}
|
||||
|
||||
func (o *RegisterAddGroup) CountToday(ctx context.Context) (int64, error) {
|
||||
now := time.Now()
|
||||
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
todayEnd := todayStart.Add(24 * time.Hour)
|
||||
filter := bson.M{
|
||||
"create_time": bson.M{
|
||||
"$gte": todayStart,
|
||||
"$lt": todayEnd,
|
||||
},
|
||||
}
|
||||
return mongoutil.Count(ctx, o.coll, filter)
|
||||
}
|
||||
65
pkg/common/db/model/bot/agent.go
Normal file
65
pkg/common/db/model/bot/agent.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/bot"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func NewAgent(db *mongo.Database) (bot.AgentInterface, error) {
|
||||
coll := db.Collection("agent")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Agent{coll: coll}, nil
|
||||
}
|
||||
|
||||
type Agent struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Agent) Create(ctx context.Context, elems ...*bot.Agent) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, elems)
|
||||
}
|
||||
|
||||
func (o *Agent) Take(ctx context.Context, userId string) (*bot.Agent, error) {
|
||||
return mongoutil.FindOne[*bot.Agent](ctx, o.coll, bson.M{"user_id": userId})
|
||||
}
|
||||
|
||||
func (o *Agent) Find(ctx context.Context, userIDs []string) ([]*bot.Agent, error) {
|
||||
return mongoutil.Find[*bot.Agent](ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *Agent) Update(ctx context.Context, userID string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *Agent) Delete(ctx context.Context, userIDs []string) error {
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *Agent) Page(ctx context.Context, userIDs []string, pagination pagination.Pagination) (int64, []*bot.Agent, error) {
|
||||
filter := bson.M{}
|
||||
if len(userIDs) > 0 {
|
||||
filter["user_id"] = bson.M{"$in": userIDs}
|
||||
}
|
||||
return mongoutil.FindPage[*bot.Agent](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
50
pkg/common/db/model/bot/conversation_resp_id.go
Normal file
50
pkg/common/db/model/bot/conversation_resp_id.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/bot"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func NewConversationRespID(db *mongo.Database) (bot.ConversationRespIDInterface, error) {
|
||||
coll := db.Collection("conversation_resp_id")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "conversation_id", Value: 1},
|
||||
{Key: "agent_id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &ConversationRespID{coll: coll}, nil
|
||||
}
|
||||
|
||||
type ConversationRespID struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *ConversationRespID) Create(ctx context.Context, elems ...*bot.ConversationRespID) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, elems)
|
||||
}
|
||||
|
||||
func (o *ConversationRespID) Take(ctx context.Context, convID, agentID string) (*bot.ConversationRespID, error) {
|
||||
return mongoutil.FindOne[*bot.ConversationRespID](ctx, o.coll, bson.M{"conversation_id": convID, "agent_id": agentID})
|
||||
}
|
||||
|
||||
func (o *ConversationRespID) Update(ctx context.Context, convID, agentID string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"conversation_id": convID, "agent_id": agentID}, bson.M{"$set": data}, false, options.Update().SetUpsert(true))
|
||||
}
|
||||
|
||||
func (o *ConversationRespID) Delete(ctx context.Context, convID, agentID string) error {
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"conversation_id": convID, "agent_id": agentID})
|
||||
}
|
||||
72
pkg/common/db/model/chat/account.go
Normal file
72
pkg/common/db/model/chat/account.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewAccount(db *mongo.Database) (chat.AccountInterface, error) {
|
||||
coll := db.Collection("account")
|
||||
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Account{coll: coll}, nil
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Account) Create(ctx context.Context, accounts ...*chat.Account) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, accounts)
|
||||
}
|
||||
|
||||
func (o *Account) Take(ctx context.Context, userId string) (*chat.Account, error) {
|
||||
return mongoutil.FindOne[*chat.Account](ctx, o.coll, bson.M{"user_id": userId})
|
||||
}
|
||||
|
||||
func (o *Account) Update(ctx context.Context, userID string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *Account) UpdatePassword(ctx context.Context, userId string, password string) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userId}, bson.M{"$set": bson.M{"password": password, "change_time": time.Now()}}, false)
|
||||
}
|
||||
|
||||
func (o *Account) Delete(ctx context.Context, userIDs []string) error {
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
247
pkg/common/db/model/chat/attribute.go
Normal file
247
pkg/common/db/model/chat/attribute.go
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewAttribute(db *mongo.Database) (chat.AttributeInterface, error) {
|
||||
coll := db.Collection("attribute")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "account", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "email", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "area_code", Value: 1},
|
||||
{Key: "phone_number", Value: 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Attribute{coll: coll}, nil
|
||||
}
|
||||
|
||||
type Attribute struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Attribute) Create(ctx context.Context, attribute ...*chat.Attribute) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, attribute)
|
||||
}
|
||||
|
||||
func (o *Attribute) Update(ctx context.Context, userID string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *Attribute) Find(ctx context.Context, userIds []string) ([]*chat.Attribute, error) {
|
||||
return mongoutil.Find[*chat.Attribute](ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIds}})
|
||||
}
|
||||
|
||||
func (o *Attribute) FindAccount(ctx context.Context, accounts []string) ([]*chat.Attribute, error) {
|
||||
return mongoutil.Find[*chat.Attribute](ctx, o.coll, bson.M{"account": bson.M{"$in": accounts}})
|
||||
}
|
||||
|
||||
func (o *Attribute) FindPhone(ctx context.Context, phoneNumbers []string) ([]*chat.Attribute, error) {
|
||||
return mongoutil.Find[*chat.Attribute](ctx, o.coll, bson.M{"phone_number": bson.M{"$in": phoneNumbers}})
|
||||
}
|
||||
|
||||
func (o *Attribute) Search(ctx context.Context, keyword string, genders []int32, pagination pagination.Pagination) (int64, []*chat.Attribute, error) {
|
||||
filter := bson.M{}
|
||||
if len(genders) > 0 {
|
||||
filter["gender"] = bson.M{
|
||||
"$in": genders,
|
||||
}
|
||||
}
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"account": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"nickname": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"phone_number": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Attribute](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *Attribute) TakePhone(ctx context.Context, areaCode string, phoneNumber string) (*chat.Attribute, error) {
|
||||
return mongoutil.FindOne[*chat.Attribute](ctx, o.coll, bson.M{"area_code": areaCode, "phone_number": phoneNumber})
|
||||
}
|
||||
|
||||
func (o *Attribute) TakeEmail(ctx context.Context, email string) (*chat.Attribute, error) {
|
||||
return mongoutil.FindOne[*chat.Attribute](ctx, o.coll, bson.M{"email": email})
|
||||
}
|
||||
|
||||
func (o *Attribute) TakeAccount(ctx context.Context, account string) (*chat.Attribute, error) {
|
||||
return mongoutil.FindOne[*chat.Attribute](ctx, o.coll, bson.M{"account": account})
|
||||
}
|
||||
|
||||
func (o *Attribute) Take(ctx context.Context, userID string) (*chat.Attribute, error) {
|
||||
return mongoutil.FindOne[*chat.Attribute](ctx, o.coll, bson.M{"user_id": userID})
|
||||
}
|
||||
|
||||
func (o *Attribute) SearchNormalUser(ctx context.Context, keyword string, forbiddenIDs []string, gender int32, startTime, endTime *time.Time, pagination pagination.Pagination) (int64, []*chat.Attribute, error) {
|
||||
filter := bson.M{}
|
||||
if gender == 0 {
|
||||
filter["gender"] = bson.M{
|
||||
"$in": []int32{0, 1, 2},
|
||||
}
|
||||
} else {
|
||||
filter["gender"] = gender
|
||||
}
|
||||
if len(forbiddenIDs) > 0 {
|
||||
filter["user_id"] = bson.M{
|
||||
"$nin": forbiddenIDs,
|
||||
}
|
||||
}
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"account": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"nickname": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"phone_number": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"email": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
// 注册时间范围查询
|
||||
if startTime != nil || endTime != nil {
|
||||
timeFilter := bson.M{}
|
||||
if startTime != nil {
|
||||
timeFilter["$gte"] = *startTime
|
||||
}
|
||||
if endTime != nil {
|
||||
// 使用 $lt(小于)而不是 $lte,确保不包含结束时间当天的数据
|
||||
// 例如:endTime="2025-11-02 00:00:00" 应该查询到 2025-11-01 23:59:59,不包含 11月2日的数据
|
||||
timeFilter["$lt"] = *endTime
|
||||
}
|
||||
if len(timeFilter) > 0 {
|
||||
filter["create_time"] = timeFilter
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Attribute](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
// SearchNormalUserWithUserIDs 按条件搜索用户(支持额外的userIDs过滤)
|
||||
func (o *Attribute) SearchNormalUserWithUserIDs(ctx context.Context, keyword string, forbiddenIDs []string, gender int32, startTime, endTime *time.Time, userIDs []string, pagination pagination.Pagination) (int64, []*chat.Attribute, error) {
|
||||
filter := bson.M{}
|
||||
if gender == 0 {
|
||||
filter["gender"] = bson.M{
|
||||
"$in": []int32{0, 1, 2},
|
||||
}
|
||||
} else {
|
||||
filter["gender"] = gender
|
||||
}
|
||||
|
||||
// 构建user_id过滤条件:需要同时满足在userIDs中,且不在forbiddenIDs中
|
||||
userIDConditions := []bson.M{}
|
||||
if len(userIDs) > 0 {
|
||||
userIDConditions = append(userIDConditions, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
if len(forbiddenIDs) > 0 {
|
||||
userIDConditions = append(userIDConditions, bson.M{"user_id": bson.M{"$nin": forbiddenIDs}})
|
||||
}
|
||||
if len(userIDConditions) > 0 {
|
||||
if len(userIDConditions) == 1 {
|
||||
filter["user_id"] = userIDConditions[0]["user_id"]
|
||||
} else {
|
||||
// 需要同时满足多个条件,使用 $and
|
||||
filter["$and"] = userIDConditions
|
||||
}
|
||||
}
|
||||
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"account": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"nickname": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"phone_number": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"email": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
// 注册时间范围查询
|
||||
if startTime != nil || endTime != nil {
|
||||
timeFilter := bson.M{}
|
||||
if startTime != nil {
|
||||
timeFilter["$gte"] = *startTime
|
||||
}
|
||||
if endTime != nil {
|
||||
timeFilter["$lt"] = *endTime
|
||||
}
|
||||
if len(timeFilter) > 0 {
|
||||
filter["create_time"] = timeFilter
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Attribute](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *Attribute) SearchUser(ctx context.Context, keyword string, userIDs []string, genders []int32, pagination pagination.Pagination) (int64, []*chat.Attribute, error) {
|
||||
filter := bson.M{}
|
||||
if len(genders) > 0 {
|
||||
filter["gender"] = bson.M{
|
||||
"$in": genders,
|
||||
}
|
||||
}
|
||||
if len(userIDs) > 0 {
|
||||
filter["user_id"] = bson.M{
|
||||
"$in": userIDs,
|
||||
}
|
||||
}
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"account": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"nickname": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"phone_number": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"email": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Attribute](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *Attribute) Delete(ctx context.Context, userIDs []string) error {
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
145
pkg/common/db/model/chat/credential.go
Normal file
145
pkg/common/db/model/chat/credential.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func NewCredential(db *mongo.Database) (chat.CredentialInterface, error) {
|
||||
coll := db.Collection("credential")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "type", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "account", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Credential{coll: coll}, nil
|
||||
}
|
||||
|
||||
type Credential struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Credential) Create(ctx context.Context, credential ...*chat.Credential) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, credential)
|
||||
}
|
||||
|
||||
func (o *Credential) CreateOrUpdateAccount(ctx context.Context, credential *chat.Credential) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{
|
||||
"user_id": credential.UserID,
|
||||
"type": credential.Type,
|
||||
}, bson.M{
|
||||
"$set": bson.M{
|
||||
"account": credential.Account,
|
||||
},
|
||||
"$setOnInsert": bson.M{
|
||||
"user_id": credential.UserID,
|
||||
"type": credential.Type,
|
||||
"allow_change": credential.AllowChange,
|
||||
},
|
||||
}, false, options.Update().SetUpsert(true))
|
||||
}
|
||||
|
||||
func (o *Credential) Update(ctx context.Context, userID string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *Credential) Find(ctx context.Context, userID string) ([]*chat.Credential, error) {
|
||||
return mongoutil.Find[*chat.Credential](ctx, o.coll, bson.M{"user_id": userID})
|
||||
}
|
||||
|
||||
func (o *Credential) FindAccount(ctx context.Context, accounts []string) ([]*chat.Credential, error) {
|
||||
return mongoutil.Find[*chat.Credential](ctx, o.coll, bson.M{"account": bson.M{"$in": accounts}})
|
||||
}
|
||||
|
||||
func (o *Credential) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*chat.Credential, error) {
|
||||
return o.SearchUser(ctx, keyword, nil, pagination)
|
||||
}
|
||||
|
||||
func (o *Credential) TakeAccount(ctx context.Context, account string) (*chat.Credential, error) {
|
||||
return mongoutil.FindOne[*chat.Credential](ctx, o.coll, bson.M{"account": account})
|
||||
}
|
||||
|
||||
func (o *Credential) Take(ctx context.Context, userID string) (*chat.Credential, error) {
|
||||
return mongoutil.FindOne[*chat.Credential](ctx, o.coll, bson.M{"user_id": userID})
|
||||
}
|
||||
|
||||
func (o *Credential) SearchNormalUser(ctx context.Context, keyword string, forbiddenIDs []string, pagination pagination.Pagination) (int64, []*chat.Credential, error) {
|
||||
filter := bson.M{}
|
||||
|
||||
if len(forbiddenIDs) > 0 {
|
||||
filter["user_id"] = bson.M{
|
||||
"$nin": forbiddenIDs,
|
||||
}
|
||||
}
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"account": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Credential](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *Credential) SearchUser(ctx context.Context, keyword string, userIDs []string, pagination pagination.Pagination) (int64, []*chat.Credential, error) {
|
||||
filter := bson.M{}
|
||||
|
||||
if len(userIDs) > 0 {
|
||||
filter["user_id"] = bson.M{
|
||||
"$in": userIDs,
|
||||
}
|
||||
}
|
||||
if keyword != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"user_id": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"account": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Credential](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *Credential) Delete(ctx context.Context, userIDs []string) error {
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *Credential) DeleteByUserIDType(ctx context.Context, credentials ...*chat.Credential) error {
|
||||
if len(credentials) == 0 {
|
||||
return nil
|
||||
}
|
||||
var filters []bson.M
|
||||
for _, credential := range credentials {
|
||||
filters = append(filters, bson.M{
|
||||
"user_id": credential.UserID,
|
||||
"type": credential.Type,
|
||||
})
|
||||
}
|
||||
|
||||
query := bson.M{"$or": filters}
|
||||
|
||||
return mongoutil.DeleteMany(ctx, o.coll, query)
|
||||
}
|
||||
151
pkg/common/db/model/chat/favorite.go
Normal file
151
pkg/common/db/model/chat/favorite.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"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"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewFavorite(db *mongo.Database) (chat.FavoriteInterface, error) {
|
||||
coll := db.Collection("favorite")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "type", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "status", Value: 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Favorite{coll: coll}, nil
|
||||
}
|
||||
|
||||
type Favorite struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Favorite) Create(ctx context.Context, favorites ...*chat.Favorite) error {
|
||||
for _, favorite := range favorites {
|
||||
if favorite.ID == "" {
|
||||
favorite.ID = primitive.NewObjectID().Hex()
|
||||
}
|
||||
if favorite.CreateTime.IsZero() {
|
||||
favorite.CreateTime = time.Now()
|
||||
}
|
||||
if favorite.UpdateTime.IsZero() {
|
||||
favorite.UpdateTime = time.Now()
|
||||
}
|
||||
if favorite.Status == 0 {
|
||||
favorite.Status = 1 // 默认为正常状态
|
||||
}
|
||||
}
|
||||
return mongoutil.InsertMany(ctx, o.coll, favorites)
|
||||
}
|
||||
|
||||
func (o *Favorite) Take(ctx context.Context, favoriteID string) (*chat.Favorite, error) {
|
||||
return mongoutil.FindOne[*chat.Favorite](ctx, o.coll, bson.M{"_id": favoriteID, "status": 1})
|
||||
}
|
||||
|
||||
func (o *Favorite) FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chat.Favorite, error) {
|
||||
filter := bson.M{
|
||||
"user_id": userID,
|
||||
"status": 1,
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Favorite](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *Favorite) FindByUserIDAndType(ctx context.Context, userID string, favoriteType int32, pagination pagination.Pagination) (int64, []*chat.Favorite, error) {
|
||||
filter := bson.M{
|
||||
"user_id": userID,
|
||||
"type": favoriteType,
|
||||
"status": 1,
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Favorite](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *Favorite) SearchByKeyword(ctx context.Context, userID string, keyword string, pagination pagination.Pagination) (int64, []*chat.Favorite, error) {
|
||||
filter := bson.M{
|
||||
"user_id": userID,
|
||||
"status": 1,
|
||||
"$or": []bson.M{
|
||||
{"title": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"content": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
{"description": bson.M{"$regex": keyword, "$options": "i"}},
|
||||
},
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Favorite](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *Favorite) Update(ctx context.Context, favoriteID string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
data["update_time"] = time.Now()
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": favoriteID}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *Favorite) Delete(ctx context.Context, favoriteIDs []string) error {
|
||||
if len(favoriteIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
// 软删除:将状态设置为0
|
||||
_, err := o.coll.UpdateMany(ctx, bson.M{"_id": bson.M{"$in": favoriteIDs}}, bson.M{"$set": bson.M{"status": 0, "update_time": time.Now()}})
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
func (o *Favorite) DeleteByUserID(ctx context.Context, userID string) error {
|
||||
// 软删除:将状态设置为0
|
||||
_, err := o.coll.UpdateMany(ctx, bson.M{"user_id": userID}, bson.M{"$set": bson.M{"status": 0, "update_time": time.Now()}})
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
func (o *Favorite) CountByUserID(ctx context.Context, userID string) (int64, error) {
|
||||
return mongoutil.Count(ctx, o.coll, bson.M{"user_id": userID, "status": 1})
|
||||
}
|
||||
|
||||
func (o *Favorite) FindByTags(ctx context.Context, userID string, tags []string, pagination pagination.Pagination) (int64, []*chat.Favorite, error) {
|
||||
filter := bson.M{
|
||||
"user_id": userID,
|
||||
"status": 1,
|
||||
"tags": bson.M{"$in": tags},
|
||||
}
|
||||
return mongoutil.FindPage[*chat.Favorite](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
81
pkg/common/db/model/chat/register.go
Normal file
81
pkg/common/db/model/chat/register.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
func NewRegister(db *mongo.Database) (chat.RegisterInterface, error) {
|
||||
coll := db.Collection("register")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Register{coll: coll}, nil
|
||||
}
|
||||
|
||||
type Register struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Register) Create(ctx context.Context, registers ...*chat.Register) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, registers)
|
||||
}
|
||||
|
||||
func (o *Register) CountTotal(ctx context.Context, before *time.Time) (int64, error) {
|
||||
filter := bson.M{}
|
||||
if before != nil {
|
||||
filter["create_time"] = bson.M{"$lt": before}
|
||||
}
|
||||
return mongoutil.Count(ctx, o.coll, filter)
|
||||
}
|
||||
|
||||
func (o *Register) CountToday(ctx context.Context) (int64, error) {
|
||||
now := time.Now()
|
||||
startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
endOfDay := startOfDay.Add(24 * time.Hour)
|
||||
|
||||
filter := bson.M{
|
||||
"create_time": bson.M{
|
||||
"$gte": startOfDay,
|
||||
"$lt": endOfDay,
|
||||
},
|
||||
}
|
||||
return mongoutil.Count(ctx, o.coll, filter)
|
||||
}
|
||||
|
||||
func (o *Register) Delete(ctx context.Context, userIDs []string) error {
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
106
pkg/common/db/model/chat/scheduled_task.go
Normal file
106
pkg/common/db/model/chat/scheduled_task.go
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"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"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewScheduledTask(db *mongo.Database) (chat.ScheduledTaskInterface, error) {
|
||||
coll := db.Collection("scheduled_tasks")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "status", Value: 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &ScheduledTask{coll: coll}, nil
|
||||
}
|
||||
|
||||
type ScheduledTask struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *ScheduledTask) Create(ctx context.Context, tasks ...*chat.ScheduledTask) error {
|
||||
for _, task := range tasks {
|
||||
if task.ID == "" {
|
||||
task.ID = primitive.NewObjectID().Hex()
|
||||
}
|
||||
if task.CreateTime.IsZero() {
|
||||
task.CreateTime = time.Now()
|
||||
}
|
||||
if task.UpdateTime.IsZero() {
|
||||
task.UpdateTime = time.Now()
|
||||
}
|
||||
if task.Status == 0 {
|
||||
task.Status = 1 // 默认为启用状态
|
||||
}
|
||||
}
|
||||
return mongoutil.InsertMany(ctx, o.coll, tasks)
|
||||
}
|
||||
|
||||
func (o *ScheduledTask) Take(ctx context.Context, taskID string) (*chat.ScheduledTask, error) {
|
||||
return mongoutil.FindOne[*chat.ScheduledTask](ctx, o.coll, bson.M{"_id": taskID})
|
||||
}
|
||||
|
||||
func (o *ScheduledTask) FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chat.ScheduledTask, error) {
|
||||
filter := bson.M{
|
||||
"user_id": userID,
|
||||
}
|
||||
return mongoutil.FindPage[*chat.ScheduledTask](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *ScheduledTask) FindAll(ctx context.Context, pagination pagination.Pagination) (int64, []*chat.ScheduledTask, error) {
|
||||
filter := bson.M{}
|
||||
return mongoutil.FindPage[*chat.ScheduledTask](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *ScheduledTask) Update(ctx context.Context, taskID string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
data["update_time"] = time.Now()
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": taskID}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *ScheduledTask) Delete(ctx context.Context, taskIDs []string) error {
|
||||
if len(taskIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := o.coll.DeleteMany(ctx, bson.M{"_id": bson.M{"$in": taskIDs}})
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
422
pkg/common/db/model/chat/sensitive_word.go
Normal file
422
pkg/common/db/model/chat/sensitive_word.go
Normal file
@@ -0,0 +1,422 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func NewSensitiveWord(db *mongo.Database) (chat.SensitiveWordInterface, error) {
|
||||
coll := db.Collection("sensitive_words")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "word", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "status", Value: 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
// 创建配置集合
|
||||
configColl := db.Collection("sensitive_word_configs")
|
||||
_, err = configColl.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "enable_filter", Value: 1},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
// 检查并初始化默认配置
|
||||
ctx := context.Background()
|
||||
count, err := configColl.CountDocuments(ctx, bson.M{})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
// 创建默认配置
|
||||
defaultConfig := &chat.SensitiveWordConfig{
|
||||
ID: "default",
|
||||
EnableFilter: true,
|
||||
FilterMode: 1,
|
||||
ReplaceChar: "***",
|
||||
WhitelistUsers: []string{},
|
||||
WhitelistGroups: []string{},
|
||||
LogEnabled: true,
|
||||
AutoApprove: false,
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
_, err = configColl.InsertOne(ctx, defaultConfig)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return &SensitiveWord{coll: coll, configColl: configColl}, nil
|
||||
}
|
||||
|
||||
type SensitiveWord struct {
|
||||
coll *mongo.Collection
|
||||
configColl *mongo.Collection
|
||||
}
|
||||
|
||||
// ==================== 敏感词管理 ====================
|
||||
|
||||
func (o *SensitiveWord) CreateSensitiveWord(ctx context.Context, word *chat.SensitiveWord) error {
|
||||
word.CreateTime = time.Now()
|
||||
word.UpdateTime = time.Now()
|
||||
return mongoutil.InsertMany(ctx, o.coll, []*chat.SensitiveWord{word})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) UpdateSensitiveWord(ctx context.Context, id string, data map[string]any) error {
|
||||
data["update_time"] = time.Now()
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": id}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) DeleteSensitiveWord(ctx context.Context, ids []string) error {
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"_id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWord(ctx context.Context, id string) (*chat.SensitiveWord, error) {
|
||||
return mongoutil.FindOne[*chat.SensitiveWord](ctx, o.coll, bson.M{"_id": id})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) SearchSensitiveWords(ctx context.Context, keyword string, action int32, status int32, pagination pagination.Pagination) (int64, []*chat.SensitiveWord, error) {
|
||||
filter := bson.M{}
|
||||
if keyword != "" {
|
||||
filter["word"] = bson.M{"$regex": keyword, "$options": "i"}
|
||||
}
|
||||
if action > 0 {
|
||||
filter["action"] = action
|
||||
}
|
||||
if status > 0 {
|
||||
filter["status"] = status
|
||||
}
|
||||
|
||||
return mongoutil.FindPage[*chat.SensitiveWord](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetAllSensitiveWords(ctx context.Context) ([]*chat.SensitiveWord, error) {
|
||||
return mongoutil.Find[*chat.SensitiveWord](ctx, o.coll, bson.M{})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetEnabledSensitiveWords(ctx context.Context) ([]*chat.SensitiveWord, error) {
|
||||
return mongoutil.Find[*chat.SensitiveWord](ctx, o.coll, bson.M{"status": chat.SensitiveStatusEnabled})
|
||||
}
|
||||
|
||||
// ==================== 敏感词检测和过滤 ====================
|
||||
|
||||
func (o *SensitiveWord) CheckSensitiveWords(ctx context.Context, content string) ([]*chat.SensitiveWord, error) {
|
||||
words, err := o.GetEnabledSensitiveWords(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var matchedWords []*chat.SensitiveWord
|
||||
contentLower := strings.ToLower(content)
|
||||
|
||||
for _, word := range words {
|
||||
if strings.Contains(contentLower, strings.ToLower(word.Word)) {
|
||||
matchedWords = append(matchedWords, word)
|
||||
}
|
||||
}
|
||||
|
||||
return matchedWords, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) FilterContent(ctx context.Context, content string) (string, []*chat.SensitiveWord, error) {
|
||||
words, err := o.CheckSensitiveWords(ctx, content)
|
||||
if err != nil {
|
||||
return content, nil, err
|
||||
}
|
||||
|
||||
if len(words) == 0 {
|
||||
return content, words, nil
|
||||
}
|
||||
|
||||
filteredContent := content
|
||||
for _, word := range words {
|
||||
replaceChar := "***"
|
||||
filteredContent = strings.ReplaceAll(filteredContent, word.Word, replaceChar)
|
||||
}
|
||||
|
||||
return filteredContent, words, nil
|
||||
}
|
||||
|
||||
// ==================== 敏感词日志管理 ====================
|
||||
|
||||
func (o *SensitiveWord) CreateSensitiveWordLog(ctx context.Context, log *chat.SensitiveWordLog) error {
|
||||
log.CreateTime = time.Now()
|
||||
return mongoutil.InsertMany(ctx, o.coll.Database().Collection("sensitive_word_logs"), []*chat.SensitiveWordLog{log})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordLogs(ctx context.Context, userID, groupID string, pagination pagination.Pagination) (int64, []*chat.SensitiveWordLog, error) {
|
||||
filter := bson.M{}
|
||||
if userID != "" {
|
||||
filter["user_id"] = userID
|
||||
}
|
||||
if groupID != "" {
|
||||
filter["group_id"] = groupID
|
||||
}
|
||||
|
||||
coll := o.coll.Database().Collection("sensitive_word_logs")
|
||||
return mongoutil.FindPage[*chat.SensitiveWordLog](ctx, coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) DeleteSensitiveWordLogs(ctx context.Context, ids []string) error {
|
||||
coll := o.coll.Database().Collection("sensitive_word_logs")
|
||||
return mongoutil.DeleteMany(ctx, coll, bson.M{"_id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
// ==================== 敏感词分组管理 ====================
|
||||
|
||||
func (o *SensitiveWord) CreateSensitiveWordGroup(ctx context.Context, group *chat.SensitiveWordGroup) error {
|
||||
group.CreateTime = time.Now()
|
||||
group.UpdateTime = time.Now()
|
||||
return mongoutil.InsertMany(ctx, o.coll.Database().Collection("sensitive_word_groups"), []*chat.SensitiveWordGroup{group})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) UpdateSensitiveWordGroup(ctx context.Context, id string, data map[string]any) error {
|
||||
data["update_time"] = time.Now()
|
||||
coll := o.coll.Database().Collection("sensitive_word_groups")
|
||||
return mongoutil.UpdateOne(ctx, coll, bson.M{"_id": id}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) DeleteSensitiveWordGroup(ctx context.Context, ids []string) error {
|
||||
coll := o.coll.Database().Collection("sensitive_word_groups")
|
||||
return mongoutil.DeleteMany(ctx, coll, bson.M{"_id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordGroup(ctx context.Context, id string) (*chat.SensitiveWordGroup, error) {
|
||||
coll := o.coll.Database().Collection("sensitive_word_groups")
|
||||
return mongoutil.FindOne[*chat.SensitiveWordGroup](ctx, coll, bson.M{"_id": id})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetAllSensitiveWordGroups(ctx context.Context) ([]*chat.SensitiveWordGroup, error) {
|
||||
coll := o.coll.Database().Collection("sensitive_word_groups")
|
||||
return mongoutil.Find[*chat.SensitiveWordGroup](ctx, coll, bson.M{})
|
||||
}
|
||||
|
||||
// ==================== 敏感词配置管理 ====================
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordConfig(ctx context.Context) (*chat.SensitiveWordConfig, error) {
|
||||
// 查找配置(默认配置已在初始化时创建)
|
||||
config, err := mongoutil.FindOne[*chat.SensitiveWordConfig](ctx, o.configColl, bson.M{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) UpdateSensitiveWordConfig(ctx context.Context, config *chat.SensitiveWordConfig) error {
|
||||
config.UpdateTime = time.Now()
|
||||
if config.ID == "" {
|
||||
config.ID = "default"
|
||||
}
|
||||
filter := bson.M{"_id": config.ID}
|
||||
fmt.Println("UpdateSensitiveWordConfig", "_________55", config, o.configColl.Name())
|
||||
err := mongoutil.UpdateOne(ctx, o.configColl, filter, bson.M{"$set": config}, true)
|
||||
fmt.Println("UpdateSensitiveWordConfig", "_________44", config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) IsFilterEnabled(ctx context.Context) (bool, error) {
|
||||
config, err := o.GetSensitiveWordConfig(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return config.EnableFilter, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetFilterMode(ctx context.Context) (int32, error) {
|
||||
// 简化实现,返回默认值
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetReplaceChar(ctx context.Context) (string, error) {
|
||||
config, err := o.GetSensitiveWordConfig(ctx)
|
||||
if err != nil {
|
||||
return "***", err
|
||||
}
|
||||
if config.ReplaceChar == "" {
|
||||
return "***", nil
|
||||
}
|
||||
return config.ReplaceChar, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) IsUserInWhitelist(ctx context.Context, userID string) (bool, error) {
|
||||
config, err := o.GetSensitiveWordConfig(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, id := range config.WhitelistUsers {
|
||||
if id == userID {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) IsGroupInWhitelist(ctx context.Context, groupID string) (bool, error) {
|
||||
config, err := o.GetSensitiveWordConfig(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, id := range config.WhitelistGroups {
|
||||
if id == groupID {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ==================== 批量操作 ====================
|
||||
|
||||
func (o *SensitiveWord) BatchCreateSensitiveWords(ctx context.Context, words []*chat.SensitiveWord) error {
|
||||
now := time.Now()
|
||||
for _, word := range words {
|
||||
word.CreateTime = now
|
||||
word.UpdateTime = now
|
||||
}
|
||||
return mongoutil.InsertMany(ctx, o.coll, words)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) BatchUpdateSensitiveWords(ctx context.Context, updates map[string]map[string]any) error {
|
||||
for id, data := range updates {
|
||||
data["update_time"] = time.Now()
|
||||
err := mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": id}, bson.M{"$set": data}, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) BatchDeleteSensitiveWords(ctx context.Context, ids []string) error {
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"_id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
// ==================== 统计信息 ====================
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordStats(ctx context.Context) (map[string]int64, error) {
|
||||
stats := make(map[string]int64)
|
||||
|
||||
// 总数
|
||||
total, err := mongoutil.Count(ctx, o.coll, bson.M{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["total"] = total
|
||||
|
||||
// 启用数量
|
||||
enabled, err := mongoutil.Count(ctx, o.coll, bson.M{"status": chat.SensitiveStatusEnabled})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["enabled"] = enabled
|
||||
|
||||
// 禁用数量
|
||||
disabled, err := mongoutil.Count(ctx, o.coll, bson.M{"status": chat.SensitiveStatusDisabled})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["disabled"] = disabled
|
||||
|
||||
// 替换模式数量
|
||||
replace, err := mongoutil.Count(ctx, o.coll, bson.M{"action": chat.SensitiveActionReplace})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["replace"] = replace
|
||||
|
||||
// 拦截模式数量
|
||||
block, err := mongoutil.Count(ctx, o.coll, bson.M{"action": chat.SensitiveActionBlock})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["block"] = block
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordLogStats(ctx context.Context, startTime, endTime time.Time) (map[string]int64, error) {
|
||||
stats := make(map[string]int64)
|
||||
coll := o.coll.Database().Collection("sensitive_word_logs")
|
||||
|
||||
filter := bson.M{
|
||||
"create_time": bson.M{
|
||||
"$gte": startTime,
|
||||
"$lte": endTime,
|
||||
},
|
||||
}
|
||||
|
||||
// 总数
|
||||
total, err := mongoutil.Count(ctx, coll, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["total"] = total
|
||||
|
||||
// 替换数量
|
||||
replaceFilter := bson.M{}
|
||||
for k, v := range filter {
|
||||
replaceFilter[k] = v
|
||||
}
|
||||
replaceFilter["action"] = chat.SensitiveActionReplace
|
||||
replace, err := mongoutil.Count(ctx, coll, replaceFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["replace"] = replace
|
||||
|
||||
// 拦截数量
|
||||
blockFilter := bson.M{}
|
||||
for k, v := range filter {
|
||||
blockFilter[k] = v
|
||||
}
|
||||
blockFilter["action"] = chat.SensitiveActionBlock
|
||||
block, err := mongoutil.Count(ctx, coll, blockFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["block"] = block
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
121
pkg/common/db/model/chat/system_config.go
Normal file
121
pkg/common/db/model/chat/system_config.go
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewSystemConfig(db *mongo.Database) (chat.SystemConfigInterface, error) {
|
||||
coll := db.Collection("system_configs")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "key", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "enabled", Value: 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &SystemConfig{coll: coll}, nil
|
||||
}
|
||||
|
||||
type SystemConfig struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *SystemConfig) Create(ctx context.Context, configs ...*chat.SystemConfig) error {
|
||||
for _, config := range configs {
|
||||
if config.CreateTime.IsZero() {
|
||||
config.CreateTime = time.Now()
|
||||
}
|
||||
if config.UpdateTime.IsZero() {
|
||||
config.UpdateTime = time.Now()
|
||||
}
|
||||
}
|
||||
return mongoutil.InsertMany(ctx, o.coll, configs)
|
||||
}
|
||||
|
||||
func (o *SystemConfig) Take(ctx context.Context, key string) (*chat.SystemConfig, error) {
|
||||
return mongoutil.FindOne[*chat.SystemConfig](ctx, o.coll, bson.M{"key": key})
|
||||
}
|
||||
|
||||
func (o *SystemConfig) FindByKeys(ctx context.Context, keys []string) ([]*chat.SystemConfig, error) {
|
||||
if len(keys) == 0 {
|
||||
return []*chat.SystemConfig{}, nil
|
||||
}
|
||||
return mongoutil.Find[*chat.SystemConfig](ctx, o.coll, bson.M{"key": bson.M{"$in": keys}}, options.Find().SetSort(bson.D{{Key: "key", Value: 1}}))
|
||||
}
|
||||
|
||||
func (o *SystemConfig) FindAll(ctx context.Context, pagination pagination.Pagination) (int64, []*chat.SystemConfig, error) {
|
||||
filter := bson.M{}
|
||||
return mongoutil.FindPage[*chat.SystemConfig](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "key", Value: 1}}))
|
||||
}
|
||||
|
||||
func (o *SystemConfig) Update(ctx context.Context, key string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
data["update_time"] = time.Now()
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"key": key}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *SystemConfig) UpdateValue(ctx context.Context, key string, value string) error {
|
||||
return o.Update(ctx, key, map[string]any{"value": value})
|
||||
}
|
||||
|
||||
func (o *SystemConfig) UpdateEnabled(ctx context.Context, key string, enabled bool) error {
|
||||
return o.Update(ctx, key, map[string]any{"enabled": enabled})
|
||||
}
|
||||
|
||||
func (o *SystemConfig) Delete(ctx context.Context, keys []string) error {
|
||||
if len(keys) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := o.coll.DeleteMany(ctx, bson.M{"key": bson.M{"$in": keys}})
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
func (o *SystemConfig) GetEnabledConfigs(ctx context.Context) ([]*chat.SystemConfig, error) {
|
||||
filter := bson.M{
|
||||
"enabled": true,
|
||||
}
|
||||
return mongoutil.Find[*chat.SystemConfig](ctx, o.coll, filter, options.Find().SetSort(bson.D{{Key: "key", Value: 1}}))
|
||||
}
|
||||
|
||||
func (o *SystemConfig) GetAppConfigs(ctx context.Context) ([]*chat.SystemConfig, error) {
|
||||
filter := bson.M{
|
||||
"show_in_app": true,
|
||||
"enabled": true, // 同时要求 enabled=true
|
||||
}
|
||||
return mongoutil.Find[*chat.SystemConfig](ctx, o.coll, filter, options.Find().SetSort(bson.D{{Key: "key", Value: 1}}))
|
||||
}
|
||||
198
pkg/common/db/model/chat/user_login_record.go
Normal file
198
pkg/common/db/model/chat/user_login_record.go
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewUserLoginRecord(db *mongo.Database) (chat.UserLoginRecordInterface, error) {
|
||||
coll := db.Collection("user_login_record")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
// 用于 CountTotal 查询:根据 create_time 范围查询
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "create_time", Value: 1},
|
||||
},
|
||||
},
|
||||
// 用于 CountTodayActiveUsers 和 CountRangeEverydayTotal:根据 login_time 范围查询
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "login_time", Value: 1},
|
||||
},
|
||||
},
|
||||
// 用于 GetLatestLoginIP:根据 user_id 查询,按 login_time 降序排序
|
||||
// 同时优化聚合查询中的 user_id 分组操作
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "login_time", Value: -1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UserLoginRecord{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type UserLoginRecord struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *UserLoginRecord) Create(ctx context.Context, records ...*chat.UserLoginRecord) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, records)
|
||||
}
|
||||
|
||||
func (o *UserLoginRecord) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
||||
filter := bson.M{}
|
||||
if before != nil {
|
||||
filter["create_time"] = bson.M{"$lt": before}
|
||||
}
|
||||
return mongoutil.Count(ctx, o.coll, filter)
|
||||
}
|
||||
|
||||
func (o *UserLoginRecord) CountTodayActiveUsers(ctx context.Context) (int64, error) {
|
||||
now := time.Now()
|
||||
startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
endOfDay := startOfDay.Add(24 * time.Hour)
|
||||
|
||||
filter := bson.M{
|
||||
"login_time": bson.M{
|
||||
"$gte": startOfDay,
|
||||
"$lt": endOfDay,
|
||||
},
|
||||
}
|
||||
|
||||
// 使用聚合管道统计不同的用户数
|
||||
pipeline := []bson.M{
|
||||
{"$match": filter},
|
||||
{"$group": bson.M{
|
||||
"_id": "$user_id",
|
||||
}},
|
||||
{"$count": "count"},
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Count int64 `bson:"count"`
|
||||
}
|
||||
results, err := mongoutil.Aggregate[Result](ctx, o.coll, pipeline)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return results[0].Count, nil
|
||||
}
|
||||
|
||||
func (o *UserLoginRecord) CountRangeEverydayTotal(ctx context.Context, start *time.Time, end *time.Time) (map[string]int64, int64, error) {
|
||||
pipeline := make([]bson.M, 0, 4)
|
||||
if start != nil || end != nil {
|
||||
filter := bson.M{}
|
||||
if start != nil {
|
||||
filter["$gte"] = start
|
||||
}
|
||||
if end != nil {
|
||||
filter["$lt"] = end
|
||||
}
|
||||
pipeline = append(pipeline, bson.M{"$match": bson.M{"login_time": filter}})
|
||||
}
|
||||
pipeline = append(pipeline,
|
||||
bson.M{
|
||||
"$project": bson.M{
|
||||
"_id": 0,
|
||||
"user_id": 1,
|
||||
"login_time": bson.M{
|
||||
"$dateToString": bson.M{
|
||||
"format": "%Y-%m-%d",
|
||||
"date": "$login_time",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": bson.M{
|
||||
"user_id": "$user_id",
|
||||
"login_time": "$login_time",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
bson.M{
|
||||
"$group": bson.M{
|
||||
"_id": "$_id.login_time",
|
||||
"count": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
type Temp struct {
|
||||
ID string `bson:"_id"`
|
||||
Count int64 `bson:"count"`
|
||||
}
|
||||
res, err := mongoutil.Aggregate[Temp](ctx, o.coll, pipeline)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var loginCount int64
|
||||
countMap := make(map[string]int64, len(res))
|
||||
for _, r := range res {
|
||||
loginCount += r.Count
|
||||
countMap[r.ID] = r.Count
|
||||
}
|
||||
return countMap, loginCount, nil
|
||||
}
|
||||
|
||||
func (o *UserLoginRecord) GetLatestLoginIP(ctx context.Context, userID string) (string, error) {
|
||||
filter := bson.M{"user_id": userID}
|
||||
opts := options.FindOne().SetSort(bson.D{{Key: "login_time", Value: -1}})
|
||||
|
||||
record, err := mongoutil.FindOne[chat.UserLoginRecord](ctx, o.coll, filter, opts)
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
return "", nil // 用户没有登录记录,返回空字符串
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return record.IP, nil
|
||||
}
|
||||
|
||||
func (o *UserLoginRecord) Search(ctx context.Context, userID, ip string, pagination pagination.Pagination) (int64, []*chat.UserLoginRecord, error) {
|
||||
filter := bson.M{}
|
||||
if userID != "" {
|
||||
filter["user_id"] = userID
|
||||
}
|
||||
if ip != "" {
|
||||
filter["ip"] = ip
|
||||
}
|
||||
return mongoutil.FindPage[*chat.UserLoginRecord](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "login_time", Value: -1}}))
|
||||
}
|
||||
146
pkg/common/db/model/chat/verify_code.go
Normal file
146
pkg/common/db/model/chat/verify_code.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"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"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
type mongoVerifyCode struct {
|
||||
ID primitive.ObjectID `bson:"_id"`
|
||||
Account string `bson:"account"`
|
||||
Platform string `bson:"platform"`
|
||||
Code string `bson:"code"`
|
||||
Duration uint `bson:"duration"`
|
||||
Count int `bson:"count"`
|
||||
Used bool `bson:"used"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func NewVerifyCode(db *mongo.Database) (chat.VerifyCodeInterface, error) {
|
||||
coll := db.Collection("verify_code")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "account", Value: 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &VerifyCode{
|
||||
coll: coll,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type VerifyCode struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *VerifyCode) parseID(s string) (primitive.ObjectID, error) {
|
||||
objID, err := primitive.ObjectIDFromHex(s)
|
||||
if err != nil {
|
||||
var zero primitive.ObjectID
|
||||
return zero, errs.Wrap(err)
|
||||
}
|
||||
return objID, nil
|
||||
}
|
||||
|
||||
func (o *VerifyCode) Add(ctx context.Context, ms []*chat.VerifyCode) error {
|
||||
tmp := make([]mongoVerifyCode, 0, len(ms))
|
||||
for i, m := range ms {
|
||||
var objID primitive.ObjectID
|
||||
if m.ID == "" {
|
||||
objID = primitive.NewObjectID()
|
||||
ms[i].ID = objID.Hex()
|
||||
} else {
|
||||
var err error
|
||||
objID, err = o.parseID(m.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
tmp = append(tmp, mongoVerifyCode{
|
||||
ID: objID,
|
||||
Account: m.Account,
|
||||
Platform: m.Platform,
|
||||
Code: m.Code,
|
||||
Duration: m.Duration,
|
||||
Count: m.Count,
|
||||
Used: m.Used,
|
||||
CreateTime: m.CreateTime,
|
||||
})
|
||||
}
|
||||
return mongoutil.InsertMany(ctx, o.coll, tmp)
|
||||
}
|
||||
|
||||
func (o *VerifyCode) RangeNum(ctx context.Context, account string, start time.Time, end time.Time) (int64, error) {
|
||||
filter := bson.M{
|
||||
"account": account,
|
||||
"create_time": bson.M{
|
||||
"$gte": start,
|
||||
"$lte": end,
|
||||
},
|
||||
}
|
||||
return mongoutil.Count(ctx, o.coll, filter)
|
||||
}
|
||||
|
||||
func (o *VerifyCode) TakeLast(ctx context.Context, account string) (*chat.VerifyCode, error) {
|
||||
filter := bson.M{
|
||||
"account": account,
|
||||
}
|
||||
opt := options.FindOne().SetSort(bson.M{"_id": -1})
|
||||
last, err := mongoutil.FindOne[*mongoVerifyCode](ctx, o.coll, filter, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &chat.VerifyCode{
|
||||
ID: last.ID.Hex(),
|
||||
Account: last.Account,
|
||||
Platform: last.Platform,
|
||||
Code: last.Code,
|
||||
Duration: last.Duration,
|
||||
Count: last.Count,
|
||||
Used: last.Used,
|
||||
CreateTime: last.CreateTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *VerifyCode) Incr(ctx context.Context, id string) error {
|
||||
objID, err := o.parseID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": objID}, bson.M{"$inc": bson.M{"count": 1}}, false)
|
||||
}
|
||||
|
||||
func (o *VerifyCode) Delete(ctx context.Context, id string) error {
|
||||
objID, err := o.parseID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mongoutil.DeleteOne(ctx, o.coll, bson.M{"_id": objID})
|
||||
}
|
||||
217
pkg/common/db/model/chat/wallet.go
Normal file
217
pkg/common/db/model/chat/wallet.go
Normal file
@@ -0,0 +1,217 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
chatdb "git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewWallet(db *mongo.Database) (chatdb.WalletInterface, error) {
|
||||
coll := db.Collection("wallets")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{{Key: "user_id", Value: 1}},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Wallet{coll: coll}, nil
|
||||
}
|
||||
|
||||
type Wallet struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Wallet) Create(ctx context.Context, wallets ...*chatdb.Wallet) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, wallets)
|
||||
}
|
||||
|
||||
func (o *Wallet) Take(ctx context.Context, userID string) (*chatdb.Wallet, error) {
|
||||
return mongoutil.FindOne[*chatdb.Wallet](ctx, o.coll, bson.M{"user_id": userID})
|
||||
}
|
||||
|
||||
func (o *Wallet) Find(ctx context.Context, userIDs []string) ([]*chatdb.Wallet, error) {
|
||||
return mongoutil.Find[*chatdb.Wallet](ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *Wallet) Update(ctx context.Context, userID string, data map[string]any) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": data}, true)
|
||||
}
|
||||
|
||||
func (o *Wallet) UpdateBalance(ctx context.Context, userID string, balance int64) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": bson.M{"balance": balance}}, true)
|
||||
}
|
||||
|
||||
// IncrementBalance 原子更新余额,使用 $inc 操作符防止并发问题
|
||||
// 返回更新前后的余额
|
||||
// 如果 amount 是负数(扣款),会检查余额是否足够,余额不足时返回错误
|
||||
func (o *Wallet) IncrementBalance(ctx context.Context, userID string, amount int64) (beforeBalance int64, afterBalance int64, err error) {
|
||||
// 如果 amount 是负数(扣款),需要确保余额不会变为负数
|
||||
filter := bson.M{"user_id": userID}
|
||||
if amount < 0 {
|
||||
// 扣款时,确保余额足够(balance >= -amount,即 balance + amount >= 0)
|
||||
// 例如:余额 100,扣款 -150,则 balance >= 150 不满足,更新失败
|
||||
filter["balance"] = bson.M{"$gte": -amount}
|
||||
}
|
||||
|
||||
update := bson.M{
|
||||
"$inc": bson.M{"balance": amount},
|
||||
"$set": bson.M{"update_time": time.Now()},
|
||||
}
|
||||
|
||||
opts := options.FindOneAndUpdate().
|
||||
SetReturnDocument(options.After). // 返回更新后的文档
|
||||
SetUpsert(true) // 如果不存在则创建
|
||||
|
||||
var wallet chatdb.Wallet
|
||||
err = o.coll.FindOneAndUpdate(ctx, filter, update, opts).Decode(&wallet)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
// 如果是因为余额不足导致更新失败(filter 条件不满足)
|
||||
if amount < 0 {
|
||||
// 获取当前余额用于错误提示
|
||||
currentWallet, takeErr := o.Take(ctx, userID)
|
||||
if takeErr == nil && currentWallet != nil {
|
||||
return currentWallet.Balance, currentWallet.Balance, errs.NewCodeError(errs.ErrArgs.Code(),
|
||||
fmt.Sprintf("余额不足:当前余额为 %d 分,需要 %d 分", currentWallet.Balance, -amount))
|
||||
}
|
||||
// 如果钱包不存在,说明余额为0,无法扣款
|
||||
return 0, 0, errs.NewCodeError(errs.ErrArgs.Code(), fmt.Sprintf("余额不足:钱包不存在或余额为0,需要 %d 分", -amount))
|
||||
}
|
||||
// 如果是增加余额但钱包不存在,应该由 upsert 创建,不应该到这里
|
||||
// 如果到这里说明有其他问题
|
||||
return 0, 0, errs.NewCodeError(errs.ErrArgs.Code(), "更新钱包余额失败")
|
||||
}
|
||||
return 0, 0, errs.Wrap(err)
|
||||
}
|
||||
|
||||
// 计算更新前的余额
|
||||
beforeBalance = wallet.Balance - amount
|
||||
afterBalance = wallet.Balance
|
||||
|
||||
// 双重检查:确保余额不为负数(虽然 filter 已经保证,但为了安全再加一次检查)
|
||||
if afterBalance < 0 {
|
||||
// 如果余额为负数,回滚操作
|
||||
rollbackUpdate := bson.M{
|
||||
"$inc": bson.M{"balance": -amount}, // 回滚
|
||||
"$set": bson.M{"update_time": time.Now()},
|
||||
}
|
||||
_ = o.coll.FindOneAndUpdate(ctx, bson.M{"user_id": userID}, rollbackUpdate, options.FindOneAndUpdate().SetReturnDocument(options.After))
|
||||
return beforeBalance, beforeBalance, errs.NewCodeError(errs.ErrArgs.Code(), "余额更新后不能为负数")
|
||||
}
|
||||
|
||||
return beforeBalance, afterBalance, nil
|
||||
}
|
||||
|
||||
func (o *Wallet) UpdatePaymentPassword(ctx context.Context, userID string, paymentPassword string) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": bson.M{"payment_password": paymentPassword, "update_time": time.Now()}}, true)
|
||||
}
|
||||
|
||||
func (o *Wallet) UpdateWithdrawAccount(ctx context.Context, userID string, withdrawAccount string) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": bson.M{"withdraw_account": withdrawAccount, "update_time": time.Now()}}, true)
|
||||
}
|
||||
|
||||
func (o *Wallet) UpdateWithdrawAccountWithType(ctx context.Context, userID string, withdrawAccount string, accountType int32) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": bson.M{"withdraw_account": withdrawAccount, "withdraw_account_type": accountType, "update_time": time.Now()}}, true)
|
||||
}
|
||||
|
||||
func (o *Wallet) UpdateRealNameAuth(ctx context.Context, userID string, realNameAuth chatdb.RealNameAuth) error {
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"user_id": userID}, bson.M{"$set": bson.M{"real_name_auth": realNameAuth, "update_time": time.Now()}}, true)
|
||||
}
|
||||
|
||||
func (o *Wallet) Delete(ctx context.Context, userIDs []string) error {
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
|
||||
}
|
||||
|
||||
func (o *Wallet) Page(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.Wallet, error) {
|
||||
return mongoutil.FindPage[*chatdb.Wallet](ctx, o.coll, bson.M{}, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
// PageByRealNameAuthAuditStatus 按实名认证审核状态分页查询钱包
|
||||
// auditStatus: 0-所有审核状态,1-审核通过,2-审核拒绝
|
||||
// userID: 用户ID搜索(可选,为空时不过滤)
|
||||
func (o *Wallet) PageByRealNameAuthAuditStatus(ctx context.Context, auditStatus int32, userID string, pagination pagination.Pagination) (int64, []*chatdb.Wallet, error) {
|
||||
filter := bson.M{
|
||||
"real_name_auth.id_card": bson.M{"$ne": ""}, // 过滤身份证号不为空的(已完成实名认证)
|
||||
"$expr": bson.M{ // 身份证长度至少 6,null/非字符串时按空字符串处理,避免 count 报错
|
||||
"$gte": []any{
|
||||
bson.M{"$strLenCP": bson.M{"$ifNull": []any{"$real_name_auth.id_card", ""}}},
|
||||
6,
|
||||
},
|
||||
},
|
||||
}
|
||||
// 支持按审核状态筛选:0-待审核,1-审核通过,2-审核拒绝;auditStatus < 0 表示不过滤状态
|
||||
if auditStatus >= 0 {
|
||||
filter["real_name_auth.audit_status"] = auditStatus
|
||||
}
|
||||
// 支持按用户ID搜索
|
||||
if userID != "" {
|
||||
filter["user_id"] = userID
|
||||
}
|
||||
return mongoutil.FindPage[*chatdb.Wallet](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "update_time", Value: -1}}))
|
||||
}
|
||||
|
||||
// SearchByRealNameAuth 按实名认证信息搜索钱包(返回userIDs)
|
||||
// realNameKeyword: 真实姓名搜索关键词(可选)
|
||||
// idCardKeyword: 身份证号搜索关键词(可选)
|
||||
func (o *Wallet) SearchByRealNameAuth(ctx context.Context, realNameKeyword string, idCardKeyword string) ([]string, error) {
|
||||
filter := bson.M{
|
||||
"real_name_auth.id_card": bson.M{"$ne": ""}, // 过滤身份证号不为空的(已完成实名认证)
|
||||
"$expr": bson.M{ // 身份证长度至少 6,null/非字符串时按空字符串处理,避免 count 报错
|
||||
"$gte": []any{
|
||||
bson.M{"$strLenCP": bson.M{"$ifNull": []any{"$real_name_auth.id_card", ""}}},
|
||||
6,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// 构建搜索条件
|
||||
orConditions := []bson.M{}
|
||||
if realNameKeyword != "" {
|
||||
orConditions = append(orConditions, bson.M{"real_name_auth.name": bson.M{"$regex": realNameKeyword, "$options": "i"}})
|
||||
}
|
||||
if idCardKeyword != "" {
|
||||
orConditions = append(orConditions, bson.M{"real_name_auth.id_card": bson.M{"$regex": idCardKeyword, "$options": "i"}})
|
||||
}
|
||||
if len(orConditions) > 0 {
|
||||
filter["$or"] = orConditions
|
||||
}
|
||||
|
||||
// 只查询 user_id 字段
|
||||
opts := options.Find().SetProjection(bson.M{"user_id": 1})
|
||||
wallets, err := mongoutil.Find[*chatdb.Wallet](ctx, o.coll, filter, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userIDs := make([]string, 0, len(wallets))
|
||||
for _, wallet := range wallets {
|
||||
userIDs = append(userIDs, wallet.UserID)
|
||||
}
|
||||
return userIDs, nil
|
||||
}
|
||||
123
pkg/common/db/model/chat/wallet_balance_record.go
Normal file
123
pkg/common/db/model/chat/wallet_balance_record.go
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
chatdb "git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewWalletBalanceRecord(db *mongo.Database) (chatdb.WalletBalanceRecordInterface, error) {
|
||||
coll := db.Collection("wallet_balance_records")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "type", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "order_id", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "transaction_id", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "red_packet_id", Value: 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &WalletBalanceRecord{coll: coll}, nil
|
||||
}
|
||||
|
||||
type WalletBalanceRecord struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) Create(ctx context.Context, records ...*chatdb.WalletBalanceRecord) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, records)
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) Take(ctx context.Context, recordID string) (*chatdb.WalletBalanceRecord, error) {
|
||||
return mongoutil.FindOne[*chatdb.WalletBalanceRecord](ctx, o.coll, bson.M{"_id": recordID})
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.WalletBalanceRecord, error) {
|
||||
filter := bson.M{"user_id": userID}
|
||||
return mongoutil.FindPage[*chatdb.WalletBalanceRecord](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) FindByUserIDAndType(ctx context.Context, userID string, recordType int32, pagination pagination.Pagination) (int64, []*chatdb.WalletBalanceRecord, error) {
|
||||
filter := bson.M{
|
||||
"user_id": userID,
|
||||
"type": recordType,
|
||||
}
|
||||
return mongoutil.FindPage[*chatdb.WalletBalanceRecord](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) FindByOrderID(ctx context.Context, orderID string) (*chatdb.WalletBalanceRecord, error) {
|
||||
return mongoutil.FindOne[*chatdb.WalletBalanceRecord](ctx, o.coll, bson.M{"order_id": orderID})
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) FindByTransactionID(ctx context.Context, transactionID string) (*chatdb.WalletBalanceRecord, error) {
|
||||
return mongoutil.FindOne[*chatdb.WalletBalanceRecord](ctx, o.coll, bson.M{"transaction_id": transactionID})
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) FindByRedPacketID(ctx context.Context, redPacketID string) ([]*chatdb.WalletBalanceRecord, error) {
|
||||
return mongoutil.Find[*chatdb.WalletBalanceRecord](ctx, o.coll, bson.M{"red_packet_id": redPacketID}, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) GetUserBalanceHistory(ctx context.Context, userID string, startTime, endTime *time.Time, pagination pagination.Pagination) (int64, []*chatdb.WalletBalanceRecord, error) {
|
||||
filter := bson.M{"user_id": userID}
|
||||
if startTime != nil || endTime != nil {
|
||||
timeFilter := bson.M{}
|
||||
if startTime != nil {
|
||||
timeFilter["$gte"] = *startTime
|
||||
}
|
||||
if endTime != nil {
|
||||
timeFilter["$lte"] = *endTime
|
||||
}
|
||||
filter["create_time"] = timeFilter
|
||||
}
|
||||
return mongoutil.FindPage[*chatdb.WalletBalanceRecord](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *WalletBalanceRecord) CountByUserID(ctx context.Context, userID string) (int64, error) {
|
||||
return mongoutil.Count(ctx, o.coll, bson.M{"user_id": userID})
|
||||
}
|
||||
95
pkg/common/db/model/chat/withdraw.go
Normal file
95
pkg/common/db/model/chat/withdraw.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
chatdb "git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewWithdraw(db *mongo.Database) (chatdb.WithdrawInterface, error) {
|
||||
coll := db.Collection("withdraws")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "status", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &Withdraw{coll: coll}, nil
|
||||
}
|
||||
|
||||
type Withdraw struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *Withdraw) Create(ctx context.Context, withdraws ...*chatdb.Withdraw) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, withdraws)
|
||||
}
|
||||
|
||||
func (o *Withdraw) Take(ctx context.Context, withdrawID string) (*chatdb.Withdraw, error) {
|
||||
return mongoutil.FindOne[*chatdb.Withdraw](ctx, o.coll, bson.M{"_id": withdrawID})
|
||||
}
|
||||
|
||||
func (o *Withdraw) FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) {
|
||||
filter := bson.M{"user_id": userID}
|
||||
return mongoutil.FindPage[*chatdb.Withdraw](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *Withdraw) FindByStatus(ctx context.Context, status int32, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) {
|
||||
filter := bson.M{"status": status}
|
||||
return mongoutil.FindPage[*chatdb.Withdraw](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *Withdraw) UpdateStatus(ctx context.Context, withdrawID string, status int32, auditorID string, auditRemark string) error {
|
||||
update := bson.M{
|
||||
"$set": bson.M{
|
||||
"status": status,
|
||||
"auditor_id": auditorID,
|
||||
"audit_time": time.Now(),
|
||||
"audit_remark": auditRemark,
|
||||
"update_time": time.Now(),
|
||||
},
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": withdrawID}, update, false)
|
||||
}
|
||||
|
||||
func (o *Withdraw) Page(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.Withdraw, error) {
|
||||
return mongoutil.FindPage[*chatdb.Withdraw](ctx, o.coll, bson.M{}, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
103
pkg/common/db/model/chat/withdraw_application.go
Normal file
103
pkg/common/db/model/chat/withdraw_application.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
chatdb "git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
)
|
||||
|
||||
func NewWithdrawApplication(db *mongo.Database) (chatdb.WithdrawApplicationInterface, error) {
|
||||
coll := db.Collection("withdraw_applications")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "user_id", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "status", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return &WithdrawApplication{coll: coll}, nil
|
||||
}
|
||||
|
||||
type WithdrawApplication struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (o *WithdrawApplication) Create(ctx context.Context, applications ...*chatdb.WithdrawApplication) error {
|
||||
return mongoutil.InsertMany(ctx, o.coll, applications)
|
||||
}
|
||||
|
||||
func (o *WithdrawApplication) Take(ctx context.Context, applicationID string) (*chatdb.WithdrawApplication, error) {
|
||||
return mongoutil.FindOne[*chatdb.WithdrawApplication](ctx, o.coll, bson.M{"_id": applicationID})
|
||||
}
|
||||
|
||||
func (o *WithdrawApplication) FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) {
|
||||
filter := bson.M{"user_id": userID}
|
||||
return mongoutil.FindPage[*chatdb.WithdrawApplication](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *WithdrawApplication) FindByStatus(ctx context.Context, status int32, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) {
|
||||
filter := bson.M{"status": status}
|
||||
return mongoutil.FindPage[*chatdb.WithdrawApplication](ctx, o.coll, filter, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *WithdrawApplication) UpdateStatus(ctx context.Context, applicationID string, status int32, auditorID string, auditRemark string) error {
|
||||
update := bson.M{
|
||||
"$set": bson.M{
|
||||
"status": status,
|
||||
"auditor_id": auditorID,
|
||||
"audit_time": time.Now(),
|
||||
"audit_remark": auditRemark,
|
||||
"update_time": time.Now(),
|
||||
},
|
||||
}
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": applicationID}, update, false)
|
||||
}
|
||||
|
||||
func (o *WithdrawApplication) Page(ctx context.Context, pagination pagination.Pagination) (int64, []*chatdb.WithdrawApplication, error) {
|
||||
return mongoutil.FindPage[*chatdb.WithdrawApplication](ctx, o.coll, bson.M{}, pagination, options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (o *WithdrawApplication) Update(ctx context.Context, applicationID string, data map[string]any) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
data["update_time"] = time.Now()
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": applicationID}, bson.M{"$set": data}, false)
|
||||
}
|
||||
51
pkg/common/db/table/admin/admin.go
Normal file
51
pkg/common/db/table/admin/admin.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
// Admin user
|
||||
type Admin struct {
|
||||
Account string `bson:"account"`
|
||||
Password string `bson:"password"`
|
||||
OperationPassword string `bson:"operation_password"`
|
||||
FaceURL string `bson:"face_url"`
|
||||
Nickname string `bson:"nickname"`
|
||||
UserID string `bson:"user_id"`
|
||||
Level int32 `bson:"level"`
|
||||
GoogleAuthKey string `bson:"google_auth_key"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (Admin) TableName() string {
|
||||
return "admins"
|
||||
}
|
||||
|
||||
type AdminInterface interface {
|
||||
Create(ctx context.Context, admins []*Admin) error
|
||||
Take(ctx context.Context, account string) (*Admin, error)
|
||||
TakeUserID(ctx context.Context, userID string) (*Admin, error)
|
||||
Update(ctx context.Context, account string, update map[string]any) error
|
||||
ChangePassword(ctx context.Context, userID string, newPassword string) error
|
||||
ChangeOperationPassword(ctx context.Context, userID string, newPassword string) error
|
||||
ClearGoogleAuthKey(ctx context.Context, userID string) error
|
||||
Delete(ctx context.Context, userIDs []string) error
|
||||
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*Admin, error)
|
||||
}
|
||||
49
pkg/common/db/table/admin/applet.go
Normal file
49
pkg/common/db/table/admin/applet.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Applet struct {
|
||||
ID string `bson:"id"`
|
||||
Name string `bson:"name"`
|
||||
AppID string `bson:"app_id"`
|
||||
Icon string `bson:"icon"`
|
||||
URL string `bson:"url"`
|
||||
MD5 string `bson:"md5"`
|
||||
Size int64 `bson:"size"`
|
||||
Version string `bson:"version"`
|
||||
Priority uint32 `bson:"priority"`
|
||||
Status uint8 `bson:"status"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (Applet) TableName() string {
|
||||
return "applets"
|
||||
}
|
||||
|
||||
type AppletInterface interface {
|
||||
Create(ctx context.Context, applets []*Applet) error
|
||||
Del(ctx context.Context, ids []string) error
|
||||
Update(ctx context.Context, id string, data map[string]any) error
|
||||
Take(ctx context.Context, id string) (*Applet, error)
|
||||
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*Applet, error)
|
||||
FindOnShelf(ctx context.Context) ([]*Applet, error)
|
||||
FindID(ctx context.Context, ids []string) ([]*Applet, error)
|
||||
}
|
||||
29
pkg/common/db/table/admin/application.go
Normal file
29
pkg/common/db/table/admin/application.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
ID primitive.ObjectID `bson:"_id"`
|
||||
Platform string `bson:"platform"`
|
||||
Hot bool `bson:"hot"`
|
||||
Version string `bson:"version"`
|
||||
Url string `bson:"url"`
|
||||
Text string `bson:"text"`
|
||||
Force bool `bson:"force"`
|
||||
Latest bool `bson:"latest"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
type ApplicationInterface interface {
|
||||
LatestVersion(ctx context.Context, platform string) (*Application, error)
|
||||
AddVersion(ctx context.Context, val *Application) error
|
||||
UpdateVersion(ctx context.Context, id primitive.ObjectID, update map[string]any) error
|
||||
DeleteVersion(ctx context.Context, id []primitive.ObjectID) error
|
||||
PageVersion(ctx context.Context, platforms []string, page pagination.Pagination) (int64, []*Application, error)
|
||||
FindPlatform(ctx context.Context, id []primitive.ObjectID) ([]string, error)
|
||||
}
|
||||
33
pkg/common/db/table/admin/client_config.go
Normal file
33
pkg/common/db/table/admin/client_config.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import "context"
|
||||
|
||||
// ClientConfig config
|
||||
type ClientConfig struct {
|
||||
Key string `bson:"key"`
|
||||
Value string `bson:"value"`
|
||||
}
|
||||
|
||||
func (ClientConfig) TableName() string {
|
||||
return "client_config"
|
||||
}
|
||||
|
||||
type ClientConfigInterface interface {
|
||||
Set(ctx context.Context, config map[string]string) error
|
||||
Get(ctx context.Context) (map[string]string, error)
|
||||
Del(ctx context.Context, keys []string) error
|
||||
}
|
||||
42
pkg/common/db/table/admin/forbidden_account.go
Normal file
42
pkg/common/db/table/admin/forbidden_account.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ForbiddenAccount table
|
||||
type ForbiddenAccount struct {
|
||||
UserID string `bson:"user_id"`
|
||||
Reason string `bson:"reason"`
|
||||
OperatorUserID string `bson:"operator_user_id"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (ForbiddenAccount) TableName() string {
|
||||
return "forbidden_accounts"
|
||||
}
|
||||
|
||||
type ForbiddenAccountInterface interface {
|
||||
Create(ctx context.Context, ms []*ForbiddenAccount) error
|
||||
Take(ctx context.Context, userID string) (*ForbiddenAccount, error)
|
||||
Delete(ctx context.Context, userIDs []string) error
|
||||
Find(ctx context.Context, userIDs []string) ([]*ForbiddenAccount, error)
|
||||
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*ForbiddenAccount, error)
|
||||
FindAllIDs(ctx context.Context) ([]string, error)
|
||||
}
|
||||
40
pkg/common/db/table/admin/invitation_register.go
Normal file
40
pkg/common/db/table/admin/invitation_register.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"time"
|
||||
)
|
||||
|
||||
type InvitationRegister struct {
|
||||
InvitationCode string `bson:"invitation_code"`
|
||||
UsedByUserID string `bson:"used_by_user_id"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (InvitationRegister) TableName() string {
|
||||
return "invitation_registers"
|
||||
}
|
||||
|
||||
type InvitationRegisterInterface interface {
|
||||
Find(ctx context.Context, codes []string) ([]*InvitationRegister, error)
|
||||
Del(ctx context.Context, codes []string) error
|
||||
Create(ctx context.Context, v []*InvitationRegister) error
|
||||
Take(ctx context.Context, code string) (*InvitationRegister, error)
|
||||
Update(ctx context.Context, code string, data map[string]any) error
|
||||
Search(ctx context.Context, keyword string, state int32, userIDs []string, codes []string, pagination pagination.Pagination) (int64, []*InvitationRegister, error)
|
||||
}
|
||||
40
pkg/common/db/table/admin/ip_forbidden.go
Normal file
40
pkg/common/db/table/admin/ip_forbidden.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IPForbidden struct {
|
||||
IP string `bson:"ip"`
|
||||
LimitRegister bool `bson:"limit_register"`
|
||||
LimitLogin bool `bson:"limit_login"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (IPForbidden) IPForbidden() string {
|
||||
return "ip_forbiddens"
|
||||
}
|
||||
|
||||
type IPForbiddenInterface interface {
|
||||
Take(ctx context.Context, ip string) (*IPForbidden, error)
|
||||
Find(ctx context.Context, ips []string) ([]*IPForbidden, error)
|
||||
Search(ctx context.Context, keyword string, state int32, pagination pagination.Pagination) (int64, []*IPForbidden, error)
|
||||
Create(ctx context.Context, ms []*IPForbidden) error
|
||||
Delete(ctx context.Context, ips []string) error
|
||||
}
|
||||
39
pkg/common/db/table/admin/limit_user_login_ip.go
Normal file
39
pkg/common/db/table/admin/limit_user_login_ip.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LimitUserLoginIP struct {
|
||||
UserID string `bson:"user_id"`
|
||||
IP string `bson:"ip"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (LimitUserLoginIP) TableName() string {
|
||||
return "limit_user_login_ips"
|
||||
}
|
||||
|
||||
type LimitUserLoginIPInterface interface {
|
||||
Create(ctx context.Context, ms []*LimitUserLoginIP) error
|
||||
Delete(ctx context.Context, ms []*LimitUserLoginIP) error
|
||||
Count(ctx context.Context, userID string) (uint32, error)
|
||||
Take(ctx context.Context, userID string, ip string) (*LimitUserLoginIP, error)
|
||||
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*LimitUserLoginIP, error)
|
||||
}
|
||||
39
pkg/common/db/table/admin/register_add_friend.go
Normal file
39
pkg/common/db/table/admin/register_add_friend.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
type RegisterAddFriend struct {
|
||||
UserID string `bson:"user_id"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (RegisterAddFriend) TableName() string {
|
||||
return "register_add_friends"
|
||||
}
|
||||
|
||||
type RegisterAddFriendInterface interface {
|
||||
Add(ctx context.Context, registerAddFriends []*RegisterAddFriend) error
|
||||
Del(ctx context.Context, userIDs []string) error
|
||||
FindUserID(ctx context.Context, userIDs []string) ([]string, error)
|
||||
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*RegisterAddFriend, error)
|
||||
CountTotal(ctx context.Context) (int64, error) // 统计好友总数
|
||||
}
|
||||
40
pkg/common/db/table/admin/register_add_group.go
Normal file
40
pkg/common/db/table/admin/register_add_group.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
type RegisterAddGroup struct {
|
||||
GroupID string `bson:"group_id"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (RegisterAddGroup) TableName() string {
|
||||
return "register_add_groups"
|
||||
}
|
||||
|
||||
type RegisterAddGroupInterface interface {
|
||||
Add(ctx context.Context, registerAddGroups []*RegisterAddGroup) error
|
||||
Del(ctx context.Context, groupIDs []string) error
|
||||
FindGroupID(ctx context.Context, groupIDs []string) ([]string, error)
|
||||
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*RegisterAddGroup, error)
|
||||
CountTotal(ctx context.Context) (int64, error) // 统计群组总数
|
||||
CountToday(ctx context.Context) (int64, error) // 统计今天新建的群组数
|
||||
}
|
||||
33
pkg/common/db/table/bot/agent.go
Normal file
33
pkg/common/db/table/bot/agent.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
type Agent struct {
|
||||
UserID string `bson:"user_id"`
|
||||
NickName string `bson:"nick_name"`
|
||||
FaceURL string `bson:"face_url"`
|
||||
Key string `bson:"key"`
|
||||
Url string `bson:"url"`
|
||||
Identity string `bson:"identity"`
|
||||
Model string `bson:"model"`
|
||||
Prompts string `bson:"prompts"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (Agent) TableName() string {
|
||||
return "agent"
|
||||
}
|
||||
|
||||
type AgentInterface interface {
|
||||
Create(ctx context.Context, elems ...*Agent) error
|
||||
Take(ctx context.Context, userID string) (*Agent, error)
|
||||
Find(ctx context.Context, userIDs []string) ([]*Agent, error)
|
||||
Update(ctx context.Context, userID string, data map[string]any) error
|
||||
Delete(ctx context.Context, userIDs []string) error
|
||||
Page(ctx context.Context, userIDs []string, pagination pagination.Pagination) (int64, []*Agent, error)
|
||||
}
|
||||
22
pkg/common/db/table/bot/conversation_resp_id.go
Normal file
22
pkg/common/db/table/bot/conversation_resp_id.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type ConversationRespID struct {
|
||||
ConversationID string `bson:"conversation_id"`
|
||||
AgentID string `bson:"agent_id"`
|
||||
PreviousResponseID string `bson:"previous_response_id"`
|
||||
}
|
||||
|
||||
func (ConversationRespID) TableName() string {
|
||||
return "conversation_resp_id"
|
||||
}
|
||||
|
||||
type ConversationRespIDInterface interface {
|
||||
Create(ctx context.Context, elems ...*ConversationRespID) error
|
||||
Take(ctx context.Context, convID, agentID string) (*ConversationRespID, error)
|
||||
Update(ctx context.Context, convID, agentID string, data map[string]any) error
|
||||
Delete(ctx context.Context, convID, agentID string) error
|
||||
}
|
||||
40
pkg/common/db/table/chat/account.go
Normal file
40
pkg/common/db/table/chat/account.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
UserID string `bson:"user_id"`
|
||||
Password string `bson:"password"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
ChangeTime time.Time `bson:"change_time"`
|
||||
OperatorUserID string `bson:"operator_user_id"`
|
||||
}
|
||||
|
||||
func (Account) TableName() string {
|
||||
return "accounts"
|
||||
}
|
||||
|
||||
type AccountInterface interface {
|
||||
Create(ctx context.Context, accounts ...*Account) error
|
||||
Take(ctx context.Context, userId string) (*Account, error)
|
||||
Update(ctx context.Context, userID string, data map[string]any) error
|
||||
UpdatePassword(ctx context.Context, userId string, password string) error
|
||||
Delete(ctx context.Context, userIDs []string) error
|
||||
}
|
||||
51
pkg/common/db/table/chat/attribute.go
Normal file
51
pkg/common/db/table/chat/attribute.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
type Attribute struct {
|
||||
UserID string `bson:"user_id"`
|
||||
Account string `bson:"account"`
|
||||
PhoneNumber string `bson:"phone_number"`
|
||||
AreaCode string `bson:"area_code"`
|
||||
Email string `bson:"email"`
|
||||
Nickname string `bson:"nickname"`
|
||||
FaceURL string `bson:"face_url"`
|
||||
Gender int32 `bson:"gender"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
ChangeTime time.Time `bson:"change_time"`
|
||||
BirthTime time.Time `bson:"birth_time"`
|
||||
Level int32 `bson:"level"`
|
||||
UserType int32 `bson:"user_type"` // 用户类型: 0=普通用户, 1=企业用户, 2=机器人, 3=管理员
|
||||
UserFlag string `bson:"user_flag"` // 用户标签/标识,类似UserType的字符串版本
|
||||
AllowVibration int32 `bson:"allow_vibration"`
|
||||
AllowBeep int32 `bson:"allow_beep"`
|
||||
AllowAddFriend int32 `bson:"allow_add_friend"`
|
||||
GlobalRecvMsgOpt int32 `bson:"global_recv_msg_opt"`
|
||||
RegisterType int32 `bson:"register_type"`
|
||||
}
|
||||
|
||||
func (Attribute) TableName() string {
|
||||
return "attributes"
|
||||
}
|
||||
|
||||
type AttributeInterface interface {
|
||||
// NewTx(tx any) AttributeInterface
|
||||
Create(ctx context.Context, attribute ...*Attribute) error
|
||||
Update(ctx context.Context, userID string, data map[string]any) error
|
||||
Find(ctx context.Context, userIds []string) ([]*Attribute, error)
|
||||
FindAccount(ctx context.Context, accounts []string) ([]*Attribute, error)
|
||||
Search(ctx context.Context, keyword string, genders []int32, pagination pagination.Pagination) (int64, []*Attribute, error)
|
||||
TakePhone(ctx context.Context, areaCode string, phoneNumber string) (*Attribute, error)
|
||||
TakeEmail(ctx context.Context, email string) (*Attribute, error)
|
||||
TakeAccount(ctx context.Context, account string) (*Attribute, error)
|
||||
Take(ctx context.Context, userID string) (*Attribute, error)
|
||||
SearchNormalUser(ctx context.Context, keyword string, forbiddenID []string, gender int32, startTime, endTime *time.Time, pagination pagination.Pagination) (int64, []*Attribute, error)
|
||||
SearchNormalUserWithUserIDs(ctx context.Context, keyword string, forbiddenID []string, gender int32, startTime, endTime *time.Time, userIDs []string, pagination pagination.Pagination) (int64, []*Attribute, error) // 按条件搜索用户(支持额外的userIDs过滤)
|
||||
SearchUser(ctx context.Context, keyword string, userIDs []string, genders []int32, pagination pagination.Pagination) (int64, []*Attribute, error)
|
||||
Delete(ctx context.Context, userIDs []string) error
|
||||
}
|
||||
32
pkg/common/db/table/chat/credential.go
Normal file
32
pkg/common/db/table/chat/credential.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
UserID string `bson:"user_id"`
|
||||
Account string `bson:"account"`
|
||||
Type int `bson:"type"` // 1:phone;2:email
|
||||
AllowChange bool `bson:"allow_change"`
|
||||
}
|
||||
|
||||
func (Credential) TableName() string {
|
||||
return "credentials"
|
||||
}
|
||||
|
||||
type CredentialInterface interface {
|
||||
Create(ctx context.Context, credential ...*Credential) error
|
||||
CreateOrUpdateAccount(ctx context.Context, credential *Credential) error
|
||||
Update(ctx context.Context, userID string, data map[string]any) error
|
||||
Find(ctx context.Context, userID string) ([]*Credential, error)
|
||||
FindAccount(ctx context.Context, accounts []string) ([]*Credential, error)
|
||||
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*Credential, error)
|
||||
TakeAccount(ctx context.Context, account string) (*Credential, error)
|
||||
Take(ctx context.Context, userID string) (*Credential, error)
|
||||
SearchNormalUser(ctx context.Context, keyword string, forbiddenID []string, pagination pagination.Pagination) (int64, []*Credential, error)
|
||||
SearchUser(ctx context.Context, keyword string, userIDs []string, pagination pagination.Pagination) (int64, []*Credential, error)
|
||||
Delete(ctx context.Context, userIDs []string) error
|
||||
DeleteByUserIDType(ctx context.Context, credentials ...*Credential) error
|
||||
}
|
||||
69
pkg/common/db/table/chat/favorite.go
Normal file
69
pkg/common/db/table/chat/favorite.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
// FavoriteType 收藏类型
|
||||
const (
|
||||
FavoriteTypeText = 1 // 文本
|
||||
FavoriteTypeImage = 2 // 图片
|
||||
FavoriteTypeLink = 3 // 链接
|
||||
FavoriteTypeFile = 4 // 文件
|
||||
FavoriteTypeVoice = 5 // 语音
|
||||
FavoriteTypeVideo = 6 // 视频
|
||||
FavoriteTypeLocation = 7 // 位置
|
||||
)
|
||||
|
||||
type Favorite struct {
|
||||
ID string `bson:"_id"` // 收藏ID(MongoDB ObjectID)
|
||||
UserID string `bson:"user_id"` // 用户ID(收藏者)
|
||||
Type int32 `bson:"type"` // 收藏类型:1-文本,2-图片,3-链接,4-文件,5-语音,6-视频,7-位置
|
||||
Title string `bson:"title"` // 标题(可选)
|
||||
Content string `bson:"content"` // 内容(根据类型不同,可能是文本、图片URL、链接URL、文件路径等)
|
||||
Description string `bson:"description"` // 摘要/描述(可选)
|
||||
Thumbnail string `bson:"thumbnail"` // 缩略图URL(用于图片、视频、链接等)
|
||||
LinkURL string `bson:"link_url"` // 链接URL(用于链接类型)
|
||||
FileSize int64 `bson:"file_size"` // 文件大小(字节,用于文件、语音、视频等)
|
||||
Duration int32 `bson:"duration"` // 时长(秒,用于语音、视频等)
|
||||
Location string `bson:"location"` // 位置信息(JSON格式,用于位置类型)
|
||||
Tags []string `bson:"tags"` // 标签列表
|
||||
Remark string `bson:"remark"` // 备注(可选)
|
||||
Status int32 `bson:"status"` // 状态:0-已删除,1-正常
|
||||
CreateTime time.Time `bson:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `bson:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
func (Favorite) TableName() string {
|
||||
return "favorites"
|
||||
}
|
||||
|
||||
type FavoriteInterface interface {
|
||||
Create(ctx context.Context, favorites ...*Favorite) error
|
||||
Take(ctx context.Context, favoriteID string) (*Favorite, error)
|
||||
FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*Favorite, error)
|
||||
FindByUserIDAndType(ctx context.Context, userID string, favoriteType int32, pagination pagination.Pagination) (int64, []*Favorite, error)
|
||||
SearchByKeyword(ctx context.Context, userID string, keyword string, pagination pagination.Pagination) (int64, []*Favorite, error)
|
||||
Update(ctx context.Context, favoriteID string, data map[string]any) error
|
||||
Delete(ctx context.Context, favoriteIDs []string) error
|
||||
DeleteByUserID(ctx context.Context, userID string) error
|
||||
CountByUserID(ctx context.Context, userID string) (int64, error)
|
||||
FindByTags(ctx context.Context, userID string, tags []string, pagination pagination.Pagination) (int64, []*Favorite, error)
|
||||
}
|
||||
67
pkg/common/db/table/chat/livekit.go
Normal file
67
pkg/common/db/table/chat/livekit.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LiveKit 表示一台LiveKit服务器配置
|
||||
type LiveKit struct {
|
||||
ID string `bson:"_id" json:"id"` // 服务器唯一标识
|
||||
Name string `bson:"name" json:"name"` // 服务器名称
|
||||
URL string `bson:"url" json:"url"` // LiveKit服务器地址
|
||||
Key string `bson:"key" json:"key"` // API Key
|
||||
Secret string `bson:"secret" json:"secret"` // API Secret
|
||||
Region string `bson:"region" json:"region"` // 服务器区域
|
||||
Status int `bson:"status" json:"status"` // 状态:0-禁用,1-启用
|
||||
Priority int `bson:"priority" json:"priority"` // 优先级,数字越小优先级越高
|
||||
MaxRooms int `bson:"max_rooms" json:"max_rooms"` // 最大房间数
|
||||
MaxUsers int `bson:"max_users" json:"max_users"` // 最大用户数
|
||||
Description string `bson:"description" json:"description"` // 描述信息
|
||||
CreateTime time.Time `bson:"create_time" json:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `bson:"update_time" json:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (LiveKit) TableName() string {
|
||||
return "livekits"
|
||||
}
|
||||
|
||||
// LiveKitInterface 定义LiveKit数据库操作接口
|
||||
type LiveKitInterface interface {
|
||||
// Create 创建LiveKit服务器配置
|
||||
Create(ctx context.Context, livekits ...*LiveKit) error
|
||||
// Delete 删除LiveKit服务器配置
|
||||
Delete(ctx context.Context, ids []string) error
|
||||
// Update 更新LiveKit服务器配置
|
||||
Update(ctx context.Context, livekit *LiveKit) error
|
||||
// FindByID 根据ID查找LiveKit配置
|
||||
FindByID(ctx context.Context, id string) (*LiveKit, error)
|
||||
// FindByStatus 根据状态查找LiveKit配置列表
|
||||
FindByStatus(ctx context.Context, status int) ([]*LiveKit, error)
|
||||
// FindAll 查找所有LiveKit配置
|
||||
FindAll(ctx context.Context) ([]*LiveKit, error)
|
||||
// FindAvailable 查找可用的LiveKit服务器(按优先级排序)
|
||||
FindAvailable(ctx context.Context) ([]*LiveKit, error)
|
||||
// FindByRegion 根据区域查找LiveKit配置
|
||||
FindByRegion(ctx context.Context, region string) ([]*LiveKit, error)
|
||||
// UpdateStatus 更新服务器状态
|
||||
UpdateStatus(ctx context.Context, id string, status int) error
|
||||
// UpdatePriority 更新服务器优先级
|
||||
UpdatePriority(ctx context.Context, id string, priority int) error
|
||||
// GetNextAvailable 获取下一个可用的LiveKit服务器(负载均衡)
|
||||
GetNextAvailable(ctx context.Context) (*LiveKit, error)
|
||||
}
|
||||
|
||||
// 状态常量
|
||||
const (
|
||||
LiveKitStatusDisabled = 0 // 禁用
|
||||
LiveKitStatusEnabled = 1 // 启用
|
||||
)
|
||||
|
||||
// 默认值
|
||||
const (
|
||||
DefaultMaxRooms = 1000 // 默认最大房间数
|
||||
DefaultMaxUsers = 100 // 默认最大用户数
|
||||
DefaultPriority = 100 // 默认优先级
|
||||
)
|
||||
42
pkg/common/db/table/chat/register.go
Normal file
42
pkg/common/db/table/chat/register.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Register struct {
|
||||
UserID string `bson:"user_id"`
|
||||
DeviceID string `bson:"device_id"`
|
||||
IP string `bson:"ip"`
|
||||
Platform string `bson:"platform"`
|
||||
AccountType string `bson:"account_type"`
|
||||
Mode string `bson:"mode"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (Register) TableName() string {
|
||||
return "registers"
|
||||
}
|
||||
|
||||
type RegisterInterface interface {
|
||||
// NewTx(tx any) RegisterInterface
|
||||
Create(ctx context.Context, registers ...*Register) error
|
||||
CountTotal(ctx context.Context, before *time.Time) (int64, error)
|
||||
CountToday(ctx context.Context) (int64, error) // 统计今天注册的用户数
|
||||
Delete(ctx context.Context, userIDs []string) error
|
||||
}
|
||||
78
pkg/common/db/table/chat/scheduled_task.go
Normal file
78
pkg/common/db/table/chat/scheduled_task.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
// ScheduledTaskStatus 定时任务状态
|
||||
const (
|
||||
ScheduledTaskStatusDisabled = 0 // 已禁用
|
||||
ScheduledTaskStatusEnabled = 1 // 已启用
|
||||
)
|
||||
|
||||
// MessageType 消息类型
|
||||
const (
|
||||
MessageTypeText = 1 // 文本消息
|
||||
MessageTypeImage = 2 // 图片消息
|
||||
MessageTypeVideo = 3 // 视频消息
|
||||
)
|
||||
|
||||
// ScheduledTask 定时任务配置
|
||||
// CronExpression格式:分 时 日 月 周
|
||||
// 例如:"0 9 * * *" 表示每天9点执行
|
||||
//
|
||||
// "*/5 * * * *" 表示每5分钟执行
|
||||
// "0 0 * * 1" 表示每周一0点执行
|
||||
type ScheduledTask struct {
|
||||
ID string `bson:"_id"` // 任务ID
|
||||
UserID string `bson:"user_id"` // 用户ID
|
||||
Name string `bson:"name"` // 任务名称
|
||||
CronExpression string `bson:"cron_expression"` // Crontab表达式:分 时 日 月 周(例如:"0 9 * * *")
|
||||
Messages []Message `bson:"messages"` // 消息列表(支持多条消息一起发送)
|
||||
RecvIDs []string `bson:"recv_ids"` // 接收者ID列表(单聊,可以多个)
|
||||
GroupIDs []string `bson:"group_ids"` // 群组ID列表(群聊,可以多个)
|
||||
Status int32 `bson:"status"` // 状态:0-已禁用,1-已启用
|
||||
CreateTime time.Time `bson:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `bson:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
// Message 消息内容
|
||||
type Message struct {
|
||||
Type int32 `bson:"type"` // 消息类型:1-文本,2-图片,3-视频
|
||||
Content string `bson:"content"` // 消息内容(文本内容、图片URL、视频URL等)
|
||||
Thumbnail string `bson:"thumbnail"` // 缩略图URL(用于图片和视频)
|
||||
Duration int32 `bson:"duration"` // 时长(秒,用于视频)
|
||||
FileSize int64 `bson:"file_size"` // 文件大小(字节,用于图片和视频)
|
||||
Width int32 `bson:"width"` // 宽度(像素,用于图片和视频)
|
||||
Height int32 `bson:"height"` // 高度(像素,用于图片和视频)
|
||||
}
|
||||
|
||||
func (ScheduledTask) TableName() string {
|
||||
return "scheduled_tasks"
|
||||
}
|
||||
|
||||
type ScheduledTaskInterface interface {
|
||||
Create(ctx context.Context, tasks ...*ScheduledTask) error
|
||||
Take(ctx context.Context, taskID string) (*ScheduledTask, error)
|
||||
FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*ScheduledTask, error)
|
||||
FindAll(ctx context.Context, pagination pagination.Pagination) (int64, []*ScheduledTask, error)
|
||||
Update(ctx context.Context, taskID string, data map[string]any) error
|
||||
Delete(ctx context.Context, taskIDs []string) error
|
||||
}
|
||||
144
pkg/common/db/table/chat/sensitive_word.go
Normal file
144
pkg/common/db/table/chat/sensitive_word.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// 敏感词相关结构体定义
|
||||
type SensitiveWord struct {
|
||||
ID string `bson:"_id" json:"id"` // 主键ID
|
||||
Word string `bson:"word" json:"word"` // 敏感词内容
|
||||
Level int32 `bson:"level" json:"level"` // 敏感词级别 1:低 2:中 3:高
|
||||
Type int32 `bson:"type" json:"type"` // 敏感词类型 1:政治 2:色情 3:暴力 4:广告 5:其他
|
||||
Action int32 `bson:"action" json:"action"` // 处理动作 1:替换为*** 2:拦截不发
|
||||
Status int32 `bson:"status" json:"status"` // 状态 1:启用 0:禁用
|
||||
Creator string `bson:"creator" json:"creator"` // 创建者
|
||||
Updater string `bson:"updater" json:"updater"` // 更新者
|
||||
CreateTime time.Time `bson:"create_time" json:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `bson:"update_time" json:"update_time"` // 更新时间
|
||||
Remark string `bson:"remark" json:"remark"` // 备注
|
||||
}
|
||||
|
||||
func (SensitiveWord) TableName() string {
|
||||
return "sensitive_words"
|
||||
}
|
||||
|
||||
type SensitiveWordLog struct {
|
||||
ID primitive.ObjectID `bson:"_id,omitempty"`
|
||||
UserID string `bson:"user_id"` // 触发用户ID
|
||||
GroupID string `bson:"group_id"` // 触发群组ID (如果是在群聊中)
|
||||
Content string `bson:"content"` // 原始消息内容
|
||||
MatchedWords []string `bson:"matched_words"` // 匹配到的敏感词
|
||||
Action int32 `bson:"action"` // 采取的动作
|
||||
ProcessedText string `bson:"processed_text"` // 处理后的文本 (如果被替换)
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (SensitiveWordLog) TableName() string {
|
||||
return "sensitive_word_logs"
|
||||
}
|
||||
|
||||
type SensitiveWordGroup struct {
|
||||
ID primitive.ObjectID `bson:"_id,omitempty"`
|
||||
Name string `bson:"name"` // 分组名称
|
||||
Remark string `bson:"remark"` // 备注
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
UpdateTime time.Time `bson:"update_time"`
|
||||
}
|
||||
|
||||
func (SensitiveWordGroup) TableName() string {
|
||||
return "sensitive_word_groups"
|
||||
}
|
||||
|
||||
type SensitiveWordConfig struct {
|
||||
ID string `bson:"_id" json:"id"` // 主键ID
|
||||
EnableFilter bool `bson:"enable_filter" json:"enable_filter"` // 是否启用过滤
|
||||
FilterMode int32 `bson:"filter_mode" json:"filter_mode"` // 过滤模式
|
||||
ReplaceChar string `bson:"replace_char" json:"replace_char"` // 替换字符,默认***
|
||||
WhitelistUsers []string `bson:"whitelist_users" json:"whitelist_users"` // 白名单用户
|
||||
WhitelistGroups []string `bson:"whitelist_groups" json:"whitelist_groups"` // 白名单群组
|
||||
LogEnabled bool `bson:"log_enabled" json:"log_enabled"` // 是否记录日志
|
||||
AutoApprove bool `bson:"auto_approve" json:"auto_approve"` // 是否自动审核
|
||||
UpdateTime time.Time `bson:"update_time" json:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
func (SensitiveWordConfig) TableName() string {
|
||||
return "sensitive_word_configs"
|
||||
}
|
||||
|
||||
// 敏感词级别常量
|
||||
const (
|
||||
SensitiveLevelLow = 1 // 低级别
|
||||
SensitiveLevelMedium = 2 // 中级别
|
||||
SensitiveLevelHigh = 3 // 高级别
|
||||
)
|
||||
|
||||
// 敏感词类型常量
|
||||
const (
|
||||
SensitiveTypePolitical = 1 // 政治
|
||||
SensitiveTypePorn = 2 // 色情
|
||||
SensitiveTypeViolence = 3 // 暴力
|
||||
SensitiveTypeAd = 4 // 广告
|
||||
SensitiveTypeOther = 5 // 其他
|
||||
)
|
||||
|
||||
// 处理动作常量
|
||||
const (
|
||||
SensitiveActionReplace = 1 // 替换为***
|
||||
SensitiveActionBlock = 2 // 拦截不发
|
||||
)
|
||||
|
||||
// 状态常量
|
||||
const (
|
||||
SensitiveStatusDisabled = 0 // 禁用
|
||||
SensitiveStatusEnabled = 1 // 启用
|
||||
)
|
||||
|
||||
type SensitiveWordInterface interface {
|
||||
// 敏感词管理
|
||||
CreateSensitiveWord(ctx context.Context, word *SensitiveWord) error
|
||||
UpdateSensitiveWord(ctx context.Context, id string, data map[string]any) error
|
||||
DeleteSensitiveWord(ctx context.Context, ids []string) error
|
||||
GetSensitiveWord(ctx context.Context, id string) (*SensitiveWord, error)
|
||||
SearchSensitiveWords(ctx context.Context, keyword string, action int32, status int32, pagination pagination.Pagination) (int64, []*SensitiveWord, error)
|
||||
GetAllSensitiveWords(ctx context.Context) ([]*SensitiveWord, error)
|
||||
GetEnabledSensitiveWords(ctx context.Context) ([]*SensitiveWord, error)
|
||||
|
||||
// 敏感词检测
|
||||
CheckSensitiveWords(ctx context.Context, content string) ([]*SensitiveWord, error)
|
||||
FilterContent(ctx context.Context, content string) (string, []*SensitiveWord, error)
|
||||
|
||||
// 敏感词日志
|
||||
CreateSensitiveWordLog(ctx context.Context, log *SensitiveWordLog) error
|
||||
GetSensitiveWordLogs(ctx context.Context, userID string, groupID string, pagination pagination.Pagination) (int64, []*SensitiveWordLog, error)
|
||||
DeleteSensitiveWordLogs(ctx context.Context, ids []string) error
|
||||
|
||||
// 敏感词分组管理
|
||||
CreateSensitiveWordGroup(ctx context.Context, group *SensitiveWordGroup) error
|
||||
UpdateSensitiveWordGroup(ctx context.Context, id string, data map[string]any) error
|
||||
DeleteSensitiveWordGroup(ctx context.Context, ids []string) error
|
||||
GetSensitiveWordGroup(ctx context.Context, id string) (*SensitiveWordGroup, error)
|
||||
GetAllSensitiveWordGroups(ctx context.Context) ([]*SensitiveWordGroup, error)
|
||||
|
||||
// 敏感词配置管理
|
||||
GetSensitiveWordConfig(ctx context.Context) (*SensitiveWordConfig, error)
|
||||
UpdateSensitiveWordConfig(ctx context.Context, config *SensitiveWordConfig) error
|
||||
IsFilterEnabled(ctx context.Context) (bool, error)
|
||||
GetFilterMode(ctx context.Context) (int32, error)
|
||||
GetReplaceChar(ctx context.Context) (string, error)
|
||||
IsUserInWhitelist(ctx context.Context, userID string) (bool, error)
|
||||
IsGroupInWhitelist(ctx context.Context, groupID string) (bool, error)
|
||||
|
||||
// 批量操作
|
||||
BatchCreateSensitiveWords(ctx context.Context, words []*SensitiveWord) error
|
||||
BatchUpdateSensitiveWords(ctx context.Context, updates map[string]map[string]any) error
|
||||
BatchDeleteSensitiveWords(ctx context.Context, ids []string) error
|
||||
|
||||
// 统计信息
|
||||
GetSensitiveWordStats(ctx context.Context) (map[string]int64, error)
|
||||
GetSensitiveWordLogStats(ctx context.Context, startTime, endTime time.Time) (map[string]int64, error)
|
||||
}
|
||||
69
pkg/common/db/table/chat/system_config.go
Normal file
69
pkg/common/db/table/chat/system_config.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
// ConfigValueType 配置值类型
|
||||
const (
|
||||
ConfigValueTypeString = 1 // 字符串类型
|
||||
ConfigValueTypeNumber = 2 // 数字类型
|
||||
ConfigValueTypeBool = 3 // 布尔类型
|
||||
ConfigValueTypeJSON = 4 // JSON类型
|
||||
)
|
||||
|
||||
// ConfigKey 常用配置键
|
||||
const (
|
||||
// 钱包相关配置
|
||||
ConfigKeyWalletEnabled = "wallet.enabled" // 是否开启钱包功能
|
||||
|
||||
// 注册相关配置
|
||||
ConfigKeyPhoneRegisterVerifyCodeEnabled = "register.phone.verify_code.enabled" // 手机号注册验证码功能是否开启
|
||||
)
|
||||
|
||||
// SystemConfig 系统配置模型
|
||||
type SystemConfig struct {
|
||||
Key string `bson:"key"` // 配置键(唯一标识)
|
||||
Title string `bson:"title"` // 配置标题
|
||||
Value string `bson:"value"` // 配置值(字符串形式存储,根据ValueType解析)
|
||||
ValueType int32 `bson:"value_type"` // 配置值类型:1-字符串,2-数字,3-布尔,4-JSON
|
||||
Description string `bson:"description"` // 配置描述
|
||||
Enabled bool `bson:"enabled"` // 是否启用(用于开关类配置)
|
||||
ShowInApp bool `bson:"show_in_app"` // 是否在APP端展示
|
||||
CreateTime time.Time `bson:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `bson:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
func (SystemConfig) TableName() string {
|
||||
return "system_configs"
|
||||
}
|
||||
|
||||
type SystemConfigInterface interface {
|
||||
Create(ctx context.Context, configs ...*SystemConfig) error
|
||||
Take(ctx context.Context, key string) (*SystemConfig, error)
|
||||
FindByKeys(ctx context.Context, keys []string) ([]*SystemConfig, error)
|
||||
FindAll(ctx context.Context, pagination pagination.Pagination) (int64, []*SystemConfig, error)
|
||||
Update(ctx context.Context, key string, data map[string]any) error
|
||||
UpdateValue(ctx context.Context, key string, value string) error
|
||||
UpdateEnabled(ctx context.Context, key string, enabled bool) error
|
||||
Delete(ctx context.Context, keys []string) error
|
||||
GetEnabledConfigs(ctx context.Context) ([]*SystemConfig, error)
|
||||
GetAppConfigs(ctx context.Context) ([]*SystemConfig, error) // 获取所有 show_in_app=true 的配置
|
||||
}
|
||||
43
pkg/common/db/table/chat/user_login_record.go
Normal file
43
pkg/common/db/table/chat/user_login_record.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
type UserLoginRecord struct {
|
||||
UserID string `bson:"user_id"`
|
||||
LoginTime time.Time `bson:"login_time"`
|
||||
IP string `bson:"ip"`
|
||||
DeviceID string `bson:"device_id"`
|
||||
Platform string `bson:"platform"`
|
||||
}
|
||||
|
||||
func (UserLoginRecord) TableName() string {
|
||||
return "user_login_records"
|
||||
}
|
||||
|
||||
type UserLoginRecordInterface interface {
|
||||
Create(ctx context.Context, records ...*UserLoginRecord) error
|
||||
CountTotal(ctx context.Context, before *time.Time) (int64, error)
|
||||
CountRangeEverydayTotal(ctx context.Context, start *time.Time, end *time.Time) (map[string]int64, int64, error)
|
||||
CountTodayActiveUsers(ctx context.Context) (int64, error) // 统计今天活跃用户数(今天登录的不同用户数)
|
||||
GetLatestLoginIP(ctx context.Context, userID string) (string, error) // 获取用户最新登录IP
|
||||
Search(ctx context.Context, userID, ip string, pagination pagination.Pagination) (int64, []*UserLoginRecord, error) // 查询登录记录(支持按用户ID或IP查询)
|
||||
}
|
||||
43
pkg/common/db/table/chat/verify_code.go
Normal file
43
pkg/common/db/table/chat/verify_code.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type VerifyCode struct {
|
||||
ID string `bson:"_id"`
|
||||
Account string `bson:"account"`
|
||||
Platform string `bson:"platform"`
|
||||
Code string `bson:"code"`
|
||||
Duration uint `bson:"duration"`
|
||||
Count int `bson:"count"`
|
||||
Used bool `bson:"used"`
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
}
|
||||
|
||||
func (VerifyCode) TableName() string {
|
||||
return "verify_codes"
|
||||
}
|
||||
|
||||
type VerifyCodeInterface interface {
|
||||
Add(ctx context.Context, ms []*VerifyCode) error
|
||||
RangeNum(ctx context.Context, account string, start time.Time, end time.Time) (int64, error)
|
||||
TakeLast(ctx context.Context, account string) (*VerifyCode, error)
|
||||
Incr(ctx context.Context, id string) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
}
|
||||
116
pkg/common/db/table/chat/wallet.go
Normal file
116
pkg/common/db/table/chat/wallet.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
// BalanceRecordType 余额变动类型
|
||||
const (
|
||||
BalanceRecordTypeRecharge = 1 // 充值
|
||||
BalanceRecordTypeWithdraw = 2 // 提现/提款
|
||||
BalanceRecordTypeConsume = 3 // 消费
|
||||
BalanceRecordTypeRefund = 4 // 退款
|
||||
BalanceRecordTypeReward = 5 // 奖励
|
||||
BalanceRecordTypeAdminRecharge = 6 // 后台充值
|
||||
BalanceRecordTypeSendRedPacket = 7 // 发红包(减少余额)
|
||||
BalanceRecordTypeGrabRedPacket = 8 // 抢红包(增加余额)
|
||||
BalanceRecordTypeOther = 99 // 其他
|
||||
)
|
||||
|
||||
// WithdrawAccountType 提现账号类型
|
||||
const (
|
||||
WithdrawAccountTypeAlipay = 1 // 支付宝
|
||||
WithdrawAccountTypeWeChat = 2 // 微信
|
||||
WithdrawAccountTypeBankCard = 3 // 银行卡
|
||||
)
|
||||
|
||||
// RealNameAuth 实名认证信息
|
||||
type RealNameAuth struct {
|
||||
IDCard string `bson:"id_card"` // 身份证号
|
||||
IDCardPhotoFront string `bson:"id_card_photo_front"` // 身份证正面照片URL
|
||||
IDCardPhotoBack string `bson:"id_card_photo_back"` // 身份证反面照片URL
|
||||
Name string `bson:"name"` // 真实姓名
|
||||
AuditStatus int32 `bson:"audit_status"` // 审核状态:0-未审核,1-审核通过,2-审核拒绝
|
||||
}
|
||||
|
||||
// Wallet 钱包模型
|
||||
type Wallet struct {
|
||||
UserID string `bson:"user_id"` // 用户ID
|
||||
Balance int64 `bson:"balance"` // 用户余额(单位:分)
|
||||
PaymentPassword string `bson:"payment_password"` // 支付密码(加密存储)
|
||||
WithdrawAccount string `bson:"withdraw_account"` // 提现账号
|
||||
WithdrawAccountType int32 `bson:"withdraw_account_type"` // 提现账号类型:1-支付宝,2-微信,3-银行卡
|
||||
RealNameAuth RealNameAuth `bson:"real_name_auth"` // 实名认证信息
|
||||
WithdrawReceiveAccount string `bson:"withdraw_receive_account"` // 提现收款账号
|
||||
CreateTime time.Time `bson:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `bson:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
func (Wallet) TableName() string {
|
||||
return "wallets"
|
||||
}
|
||||
|
||||
type WalletInterface interface {
|
||||
Create(ctx context.Context, wallets ...*Wallet) error
|
||||
Take(ctx context.Context, userID string) (*Wallet, error)
|
||||
Find(ctx context.Context, userIDs []string) ([]*Wallet, error)
|
||||
Update(ctx context.Context, userID string, data map[string]any) error
|
||||
UpdateBalance(ctx context.Context, userID string, balance int64) error
|
||||
IncrementBalance(ctx context.Context, userID string, amount int64) (beforeBalance int64, afterBalance int64, err error) // 原子更新余额,返回更新前后的余额
|
||||
UpdatePaymentPassword(ctx context.Context, userID string, paymentPassword string) error
|
||||
UpdateWithdrawAccount(ctx context.Context, userID string, withdrawAccount string) error // 更新提款账号(兼容旧接口)
|
||||
UpdateWithdrawAccountWithType(ctx context.Context, userID string, withdrawAccount string, accountType int32) error // 更新提款账号(带类型)
|
||||
UpdateRealNameAuth(ctx context.Context, userID string, realNameAuth RealNameAuth) error // 更新实名认证信息
|
||||
Delete(ctx context.Context, userIDs []string) error
|
||||
Page(ctx context.Context, pagination pagination.Pagination) (int64, []*Wallet, error)
|
||||
PageByRealNameAuthAuditStatus(ctx context.Context, auditStatus int32, userID string, pagination pagination.Pagination) (int64, []*Wallet, error) // 按实名认证审核状态分页查询(支持用户ID搜索)
|
||||
SearchByRealNameAuth(ctx context.Context, realNameKeyword string, idCardKeyword string) ([]string, error) // 按实名认证信息搜索钱包(返回userIDs)
|
||||
}
|
||||
|
||||
// WalletBalanceRecord 钱包余额记录
|
||||
type WalletBalanceRecord struct {
|
||||
ID string `bson:"_id"` // 记录ID
|
||||
UserID string `bson:"user_id"` // 用户ID
|
||||
Amount int64 `bson:"amount"` // 变动金额(单位:分,正数表示增加,负数表示减少)
|
||||
Type int32 `bson:"type"` // 变动类型:1-充值,2-提现/提款,3-消费,4-退款,5-奖励,6-后台充值,7-发红包,8-抢红包,99-其他
|
||||
BeforeBalance int64 `bson:"before_balance"` // 变动前余额(单位:分)
|
||||
AfterBalance int64 `bson:"after_balance"` // 变动后余额(单位:分)
|
||||
OrderID string `bson:"order_id"` // 关联订单ID(可选)
|
||||
TransactionID string `bson:"transaction_id"` // 交易ID(可选)
|
||||
RedPacketID string `bson:"red_packet_id"` // 红包ID(用于发红包和抢红包记录关联,可选)
|
||||
Remark string `bson:"remark"` // 备注
|
||||
CreateTime time.Time `bson:"create_time"` // 创建时间
|
||||
}
|
||||
|
||||
func (WalletBalanceRecord) TableName() string {
|
||||
return "wallet_balance_records"
|
||||
}
|
||||
|
||||
type WalletBalanceRecordInterface interface {
|
||||
Create(ctx context.Context, records ...*WalletBalanceRecord) error
|
||||
Take(ctx context.Context, recordID string) (*WalletBalanceRecord, error)
|
||||
FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*WalletBalanceRecord, error)
|
||||
FindByUserIDAndType(ctx context.Context, userID string, recordType int32, pagination pagination.Pagination) (int64, []*WalletBalanceRecord, error)
|
||||
FindByOrderID(ctx context.Context, orderID string) (*WalletBalanceRecord, error)
|
||||
FindByTransactionID(ctx context.Context, transactionID string) (*WalletBalanceRecord, error)
|
||||
FindByRedPacketID(ctx context.Context, redPacketID string) ([]*WalletBalanceRecord, error)
|
||||
GetUserBalanceHistory(ctx context.Context, userID string, startTime, endTime *time.Time, pagination pagination.Pagination) (int64, []*WalletBalanceRecord, error)
|
||||
CountByUserID(ctx context.Context, userID string) (int64, error)
|
||||
}
|
||||
63
pkg/common/db/table/chat/withdraw.go
Normal file
63
pkg/common/db/table/chat/withdraw.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
// WithdrawStatus 提现状态
|
||||
const (
|
||||
WithdrawStatusPending = 1 // 待审核
|
||||
WithdrawStatusApproved = 2 // 已通过
|
||||
WithdrawStatusRejected = 3 // 已拒绝
|
||||
)
|
||||
|
||||
// Withdraw 提现记录
|
||||
type Withdraw struct {
|
||||
ID string `bson:"_id"` // 提现ID
|
||||
UserID string `bson:"user_id"` // 用户ID
|
||||
Amount int64 `bson:"amount"` // 提现金额(单位:分)
|
||||
WithdrawAccount string `bson:"withdraw_account"` // 提现账号
|
||||
Status int32 `bson:"status"` // 审核状态:1-待审核,2-已通过,3-已拒绝
|
||||
AuditorID string `bson:"auditor_id"` // 审核人ID(管理员ID)
|
||||
AuditTime time.Time `bson:"audit_time"` // 审核时间
|
||||
AuditRemark string `bson:"audit_remark"` // 审核备注
|
||||
IP string `bson:"ip"` // 提现IP
|
||||
DeviceID string `bson:"device_id"` // 设备ID
|
||||
Platform string `bson:"platform"` // 平台(iOS、Android、Web等)
|
||||
DeviceModel string `bson:"device_model"` // 设备型号(如:iPhone 14 Pro、Samsung Galaxy S23等)
|
||||
DeviceBrand string `bson:"device_brand"` // 设备品牌(如:Apple、Samsung、Huawei等)
|
||||
OSVersion string `bson:"os_version"` // 操作系统版本(如:iOS 17.0、Android 13等)
|
||||
AppVersion string `bson:"app_version"` // 应用版本
|
||||
CreateTime time.Time `bson:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `bson:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
func (Withdraw) TableName() string {
|
||||
return "withdraws"
|
||||
}
|
||||
|
||||
type WithdrawInterface interface {
|
||||
Create(ctx context.Context, withdraws ...*Withdraw) error
|
||||
Take(ctx context.Context, withdrawID string) (*Withdraw, error)
|
||||
FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*Withdraw, error)
|
||||
FindByStatus(ctx context.Context, status int32, pagination pagination.Pagination) (int64, []*Withdraw, error)
|
||||
UpdateStatus(ctx context.Context, withdrawID string, status int32, auditorID string, auditRemark string) error
|
||||
Page(ctx context.Context, pagination pagination.Pagination) (int64, []*Withdraw, error)
|
||||
}
|
||||
68
pkg/common/db/table/chat/withdraw_application.go
Normal file
68
pkg/common/db/table/chat/withdraw_application.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
// WithdrawApplicationStatus 提现申请状态
|
||||
const (
|
||||
WithdrawApplicationStatusPending = 1 // 待审核
|
||||
WithdrawApplicationStatusApproved = 2 // 已通过
|
||||
WithdrawApplicationStatusRejected = 3 // 已拒绝
|
||||
WithdrawApplicationStatusProcessing = 4 // 处理中
|
||||
WithdrawApplicationStatusCompleted = 5 // 已完成
|
||||
)
|
||||
|
||||
// WithdrawApplication 提现申请
|
||||
type WithdrawApplication struct {
|
||||
ID string `bson:"_id"` // 申请ID
|
||||
UserID string `bson:"user_id"` // 用户ID
|
||||
Amount int64 `bson:"amount"` // 提现金额(单位:分)
|
||||
WithdrawAccount string `bson:"withdraw_account"` // 提现账号
|
||||
WithdrawAccountType int32 `bson:"withdraw_account_type"` // 提现账号类型:1-支付宝,2-微信,3-银行卡
|
||||
Status int32 `bson:"status"` // 申请状态:1-待审核,2-已通过,3-已拒绝,4-处理中,5-已完成
|
||||
AuditorID string `bson:"auditor_id"` // 审核人ID(管理员ID)
|
||||
AuditTime time.Time `bson:"audit_time"` // 审核时间
|
||||
AuditRemark string `bson:"audit_remark"` // 审核备注
|
||||
IP string `bson:"ip"` // 申请IP
|
||||
DeviceID string `bson:"device_id"` // 设备ID
|
||||
Platform string `bson:"platform"` // 平台(iOS、Android、Web等)
|
||||
DeviceModel string `bson:"device_model"` // 设备型号(如:iPhone 14 Pro、Samsung Galaxy S23等)
|
||||
DeviceBrand string `bson:"device_brand"` // 设备品牌(如:Apple、Samsung、Huawei等)
|
||||
OSVersion string `bson:"os_version"` // 操作系统版本(如:iOS 17.0、Android 13等)
|
||||
AppVersion string `bson:"app_version"` // 应用版本
|
||||
Remark string `bson:"remark"` // 申请备注
|
||||
CreateTime time.Time `bson:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `bson:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
func (WithdrawApplication) TableName() string {
|
||||
return "withdraw_applications"
|
||||
}
|
||||
|
||||
type WithdrawApplicationInterface interface {
|
||||
Create(ctx context.Context, applications ...*WithdrawApplication) error
|
||||
Take(ctx context.Context, applicationID string) (*WithdrawApplication, error)
|
||||
FindByUserID(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*WithdrawApplication, error)
|
||||
FindByStatus(ctx context.Context, status int32, pagination pagination.Pagination) (int64, []*WithdrawApplication, error)
|
||||
UpdateStatus(ctx context.Context, applicationID string, status int32, auditorID string, auditRemark string) error
|
||||
Page(ctx context.Context, pagination pagination.Pagination) (int64, []*WithdrawApplication, error)
|
||||
Update(ctx context.Context, applicationID string, data map[string]any) error
|
||||
}
|
||||
53
pkg/common/imapi/api.go
Normal file
53
pkg/common/imapi/api.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 imapi
|
||||
|
||||
import (
|
||||
"git.imall.cloud/openim/protocol/auth"
|
||||
"git.imall.cloud/openim/protocol/group"
|
||||
"git.imall.cloud/openim/protocol/relation"
|
||||
"git.imall.cloud/openim/protocol/user"
|
||||
)
|
||||
|
||||
// im caller.
|
||||
var (
|
||||
getAdminToken = NewApiCaller[auth.GetAdminTokenReq, auth.GetAdminTokenResp]("/auth/get_admin_token")
|
||||
getuserToken = NewApiCaller[auth.GetUserTokenReq, auth.GetUserTokenResp]("/auth/get_user_token")
|
||||
forceOffLine = NewApiCaller[auth.ForceLogoutReq, auth.ForceLogoutResp]("/auth/force_logout")
|
||||
|
||||
updateUserInfo = NewApiCaller[user.UpdateUserInfoReq, user.UpdateUserInfoResp]("/user/update_user_info")
|
||||
updateUserInfoEx = NewApiCaller[user.UpdateUserInfoExReq, user.UpdateUserInfoExResp]("/user/update_user_info_ex")
|
||||
registerUser = NewApiCaller[user.UserRegisterReq, user.UserRegisterResp]("/user/user_register")
|
||||
getUserInfo = NewApiCaller[user.GetDesignateUsersReq, user.GetDesignateUsersResp]("/user/get_users_info")
|
||||
accountCheck = NewApiCaller[user.AccountCheckReq, user.AccountCheckResp]("/user/account_check")
|
||||
addNotificationAccount = NewApiCaller[user.AddNotificationAccountReq, user.AddNotificationAccountResp]("/user/add_notification_account")
|
||||
updateNotificationAccount = NewApiCaller[user.UpdateNotificationAccountInfoReq, user.UpdateNotificationAccountInfoResp]("/user/update_notification_account")
|
||||
|
||||
getGroupsInfo = NewApiCaller[group.GetGroupsInfoReq, group.GetGroupsInfoResp]("/group/get_groups_info")
|
||||
inviteToGroup = NewApiCaller[group.InviteUserToGroupReq, group.InviteUserToGroupResp]("/group/invite_user_to_group")
|
||||
|
||||
registerUserCount = NewApiCaller[user.UserRegisterCountReq, user.UserRegisterCountResp]("/statistics/user/register")
|
||||
|
||||
onlineUserCount = NewApiCaller[OnlineUserCountReq, OnlineUserCountResp]("/statistics/online_user_count")
|
||||
onlineUserCountTrend = NewApiCaller[OnlineUserCountTrendReq, OnlineUserCountTrendResp]("/statistics/online_user_count_trend")
|
||||
userSendMsgCount = NewApiCaller[UserSendMsgCountReq, UserSendMsgCountResp]("/statistics/user_send_msg_count")
|
||||
userSendMsgCountTrend = NewApiCaller[UserSendMsgCountTrendReq, UserSendMsgCountTrendResp]("/statistics/user_send_msg_count_trend")
|
||||
userSendMsgQuery = NewApiCaller[UserSendMsgQueryReq, UserSendMsgQueryResp]("/statistics/user_send_msg_query")
|
||||
|
||||
friendUserIDs = NewApiCaller[relation.GetFriendIDsReq, relation.GetFriendIDsResp]("/friend/get_friend_id")
|
||||
importFriend = NewApiCaller[relation.ImportFriendReq, relation.ImportFriendResp]("/friend/import_friend")
|
||||
|
||||
sendSimpleMsg = NewApiCaller[SendSingleMsgReq, SendSingleMsgResp]("/msg/send_simple_msg")
|
||||
)
|
||||
160
pkg/common/imapi/call.go
Normal file
160
pkg/common/imapi/call.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 imapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
constantpb "git.imall.cloud/openim/protocol/constant"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"gorm.io/gorm/utils"
|
||||
)
|
||||
|
||||
type baseApiResponse[T any] struct {
|
||||
ErrCode int `json:"errCode"`
|
||||
ErrMsg string `json:"errMsg"`
|
||||
ErrDlt string `json:"errDlt"`
|
||||
Data *T `json:"data"`
|
||||
}
|
||||
|
||||
var client = &http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
|
||||
type ApiCaller[Req, Resp any] interface {
|
||||
Call(ctx context.Context, apiPrefix string, req *Req) (*Resp, error)
|
||||
CallWithQuery(ctx context.Context, apiPrefix string, req *Req, queryParams map[string]string) (*Resp, error)
|
||||
}
|
||||
|
||||
func NewApiCaller[Req, Resp any](api string) ApiCaller[Req, Resp] {
|
||||
return &caller[Req, Resp]{
|
||||
api: api,
|
||||
}
|
||||
}
|
||||
|
||||
type caller[Req, Resp any] struct {
|
||||
api string
|
||||
}
|
||||
|
||||
func (a caller[Req, Resp]) Call(ctx context.Context, apiPrefix string, req *Req) (*Resp, error) {
|
||||
start := time.Now()
|
||||
resp, err := a.call(ctx, apiPrefix, req)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "api caller failed", err, "api", a.api, "duration", time.Since(start), "req", req, "resp", resp)
|
||||
return nil, err
|
||||
}
|
||||
log.ZInfo(ctx, "api caller success resp", "api", a.api, "duration", time.Since(start), "req", req, "resp", resp)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (a caller[Req, Resp]) call(ctx context.Context, apiPrefix string, req *Req) (*Resp, error) {
|
||||
url := apiPrefix + a.api
|
||||
reqBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
operationID := utils.ToString(ctx.Value(constantpb.OperationID))
|
||||
request.Header.Set(constantpb.OperationID, operationID)
|
||||
if token, _ := ctx.Value(constant.CtxApiToken).(string); token != "" {
|
||||
request.Header.Set(constantpb.Token, token)
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
data, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, errs.WrapMsg(err, "read http response body", "url", url, "code", response.StatusCode)
|
||||
}
|
||||
var resp baseApiResponse[Resp]
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, errs.WrapMsg(err, string(data))
|
||||
}
|
||||
if resp.ErrCode != 0 {
|
||||
return nil, errs.NewCodeError(resp.ErrCode, resp.ErrMsg).WithDetail(resp.ErrDlt).Wrap()
|
||||
}
|
||||
return resp.Data, nil
|
||||
}
|
||||
|
||||
func (a caller[Req, Resp]) CallWithQuery(ctx context.Context, apiPrefix string, req *Req, queryParams map[string]string) (*Resp, error) {
|
||||
start := time.Now()
|
||||
resp, err := a.callWithQuery(ctx, apiPrefix, req, queryParams)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "api caller failed", err, "api", a.api, "duration", time.Since(start), "req", req, "resp", resp)
|
||||
return nil, err
|
||||
}
|
||||
log.ZInfo(ctx, "api caller success resp", "api", a.api, "duration", time.Since(start), "req", req, "resp", resp)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (a caller[Req, Resp]) callWithQuery(ctx context.Context, apiPrefix string, req *Req, queryParams map[string]string) (*Resp, error) {
|
||||
fullURL := apiPrefix + a.api
|
||||
parsedURL, err := url.Parse(fullURL)
|
||||
if err != nil {
|
||||
return nil, errs.WrapMsg(err, "failed to parse URL", fullURL)
|
||||
}
|
||||
|
||||
query := parsedURL.Query()
|
||||
|
||||
for key, value := range queryParams {
|
||||
query.Set(key, value)
|
||||
}
|
||||
|
||||
parsedURL.RawQuery = query.Encode()
|
||||
fullURL = parsedURL.String()
|
||||
reqBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, fullURL, bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
operationID := utils.ToString(ctx.Value(constantpb.OperationID))
|
||||
request.Header.Set(constantpb.OperationID, operationID)
|
||||
if token, _ := ctx.Value(constant.CtxApiToken).(string); token != "" {
|
||||
request.Header.Set(constantpb.Token, token)
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
data, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, errs.WrapMsg(err, "read http response body", "fullUrl", fullURL, "code", response.StatusCode)
|
||||
}
|
||||
var resp baseApiResponse[Resp]
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return nil, errs.WrapMsg(err, string(data))
|
||||
}
|
||||
if resp.ErrCode != 0 {
|
||||
return nil, errs.NewCodeError(resp.ErrCode, resp.ErrMsg).WithDetail(resp.ErrDlt).Wrap()
|
||||
}
|
||||
return resp.Data, nil
|
||||
}
|
||||
281
pkg/common/imapi/caller.go
Normal file
281
pkg/common/imapi/caller.go
Normal file
@@ -0,0 +1,281 @@
|
||||
package imapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/botstruct"
|
||||
"git.imall.cloud/openim/chat/pkg/eerrs"
|
||||
"git.imall.cloud/openim/protocol/auth"
|
||||
"git.imall.cloud/openim/protocol/constant"
|
||||
"git.imall.cloud/openim/protocol/group"
|
||||
"git.imall.cloud/openim/protocol/relation"
|
||||
"git.imall.cloud/openim/protocol/sdkws"
|
||||
"git.imall.cloud/openim/protocol/user"
|
||||
wrapperspb "git.imall.cloud/openim/protocol/wrapperspb"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
type CallerInterface interface {
|
||||
ImAdminTokenWithDefaultAdmin(ctx context.Context) (string, error)
|
||||
ImportFriend(ctx context.Context, ownerUserID string, friendUserID []string) error
|
||||
GetUserToken(ctx context.Context, userID string, platform int32) (string, error)
|
||||
GetAdminTokenCache(ctx context.Context, userID string) (string, error)
|
||||
GetAdminTokenServer(ctx context.Context, userID string) (string, error)
|
||||
InviteToGroup(ctx context.Context, userID string, groupIDs []string) error
|
||||
|
||||
UpdateUserInfo(ctx context.Context, userID string, nickName string, faceURL string, userType int32, userFlag string) error
|
||||
UpdateUserInfoEx(ctx context.Context, userID string, ex string) error
|
||||
GetUserInfo(ctx context.Context, userID string) (*sdkws.UserInfo, error)
|
||||
GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error)
|
||||
AddNotificationAccount(ctx context.Context, req *user.AddNotificationAccountReq) error
|
||||
UpdateNotificationAccount(ctx context.Context, req *user.UpdateNotificationAccountInfoReq) error
|
||||
|
||||
ForceOffLine(ctx context.Context, userID string) error
|
||||
RegisterUser(ctx context.Context, users []*sdkws.UserInfo) error
|
||||
FindGroupInfo(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error)
|
||||
UserRegisterCount(ctx context.Context, start int64, end int64) (map[string]int64, int64, error)
|
||||
OnlineUserCount(ctx context.Context) (*OnlineUserCountResp, error)
|
||||
OnlineUserCountTrend(ctx context.Context, req *OnlineUserCountTrendReq) (*OnlineUserCountTrendResp, error)
|
||||
UserSendMsgCount(ctx context.Context, req *UserSendMsgCountReq) (*UserSendMsgCountResp, error)
|
||||
UserSendMsgCountTrend(ctx context.Context, req *UserSendMsgCountTrendReq) (*UserSendMsgCountTrendResp, error)
|
||||
UserSendMsgQuery(ctx context.Context, req *UserSendMsgQueryReq) (*UserSendMsgQueryResp, error)
|
||||
FriendUserIDs(ctx context.Context, userID string) ([]string, error)
|
||||
AccountCheckSingle(ctx context.Context, userID string) (bool, error)
|
||||
SendSimpleMsg(ctx context.Context, req *SendSingleMsgReq, key string) error
|
||||
}
|
||||
|
||||
type authToken struct {
|
||||
token string
|
||||
expired time.Time
|
||||
}
|
||||
|
||||
type Caller struct {
|
||||
imApi string
|
||||
imSecret string
|
||||
defaultIMUserID string
|
||||
tokenCache map[string]*authToken
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func New(imApi string, imSecret string, defaultIMUserID string) CallerInterface {
|
||||
return &Caller{
|
||||
imApi: imApi,
|
||||
imSecret: imSecret,
|
||||
defaultIMUserID: defaultIMUserID,
|
||||
tokenCache: make(map[string]*authToken),
|
||||
lock: sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Caller) ImportFriend(ctx context.Context, ownerUserID string, friendUserIDs []string) error {
|
||||
if len(friendUserIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := importFriend.Call(ctx, c.imApi, &relation.ImportFriendReq{
|
||||
OwnerUserID: ownerUserID,
|
||||
FriendUserIDs: friendUserIDs,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Caller) ImAdminTokenWithDefaultAdmin(ctx context.Context) (string, error) {
|
||||
return c.GetAdminTokenCache(ctx, c.defaultIMUserID)
|
||||
}
|
||||
|
||||
func (c *Caller) GetAdminTokenCache(ctx context.Context, userID string) (string, error) {
|
||||
c.lock.RLock()
|
||||
t, ok := c.tokenCache[userID]
|
||||
c.lock.RUnlock()
|
||||
if ok && t.expired.After(time.Now()) {
|
||||
return t.token, nil
|
||||
}
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
t, ok = c.tokenCache[userID]
|
||||
if ok && t.expired.After(time.Now()) {
|
||||
return t.token, nil
|
||||
}
|
||||
token, err := c.GetAdminTokenServer(ctx, userID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
c.tokenCache[userID] = &authToken{token: token, expired: time.Now().Add(time.Minute * 4)}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (c *Caller) GetAdminTokenServer(ctx context.Context, userID string) (string, error) {
|
||||
resp, err := getAdminToken.Call(ctx, c.imApi, &auth.GetAdminTokenReq{
|
||||
Secret: c.imSecret,
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.ZDebug(ctx, "get im admin token from server", "userID", userID, "token", resp.Token)
|
||||
return resp.Token, nil
|
||||
}
|
||||
|
||||
func (c *Caller) GetUserToken(ctx context.Context, userID string, platformID int32) (string, error) {
|
||||
resp, err := getuserToken.Call(ctx, c.imApi, &auth.GetUserTokenReq{
|
||||
PlatformID: platformID,
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Token, nil
|
||||
}
|
||||
|
||||
func (c *Caller) InviteToGroup(ctx context.Context, userID string, groupIDs []string) error {
|
||||
for _, groupID := range groupIDs {
|
||||
_, _ = inviteToGroup.Call(ctx, c.imApi, &group.InviteUserToGroupReq{
|
||||
GroupID: groupID,
|
||||
Reason: "",
|
||||
InvitedUserIDs: []string{userID},
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Caller) UpdateUserInfo(ctx context.Context, userID string, nickName string, faceURL string, userType int32, userFlag string) error {
|
||||
_, err := updateUserInfo.Call(ctx, c.imApi, &user.UpdateUserInfoReq{UserInfo: &sdkws.UserInfo{
|
||||
UserID: userID,
|
||||
Nickname: nickName,
|
||||
FaceURL: faceURL,
|
||||
UserType: userType,
|
||||
UserFlag: userFlag,
|
||||
}})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Caller) UpdateUserInfoEx(ctx context.Context, userID string, ex string) error {
|
||||
_, err := updateUserInfoEx.Call(ctx, c.imApi, &user.UpdateUserInfoExReq{
|
||||
UserInfo: &sdkws.UserInfoWithEx{
|
||||
UserID: userID,
|
||||
Ex: &wrapperspb.StringValue{Value: ex},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Caller) GetUserInfo(ctx context.Context, userID string) (*sdkws.UserInfo, error) {
|
||||
resp, err := c.GetUsersInfo(ctx, []string{userID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp) == 0 {
|
||||
return nil, errs.ErrRecordNotFound.WrapMsg("record not found")
|
||||
}
|
||||
return resp[0], nil
|
||||
}
|
||||
|
||||
func (c *Caller) GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) {
|
||||
resp, err := getUserInfo.Call(ctx, c.imApi, &user.GetDesignateUsersReq{
|
||||
UserIDs: userIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.UsersInfo, nil
|
||||
}
|
||||
|
||||
func (c *Caller) RegisterUser(ctx context.Context, users []*sdkws.UserInfo) error {
|
||||
_, err := registerUser.Call(ctx, c.imApi, &user.UserRegisterReq{
|
||||
Users: users,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Caller) ForceOffLine(ctx context.Context, userID string) error {
|
||||
for id := range constant.PlatformID2Name {
|
||||
_, _ = forceOffLine.Call(ctx, c.imApi, &auth.ForceLogoutReq{
|
||||
PlatformID: int32(id),
|
||||
UserID: userID,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Caller) FindGroupInfo(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) {
|
||||
resp, err := getGroupsInfo.Call(ctx, c.imApi, &group.GetGroupsInfoReq{
|
||||
GroupIDs: groupIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.GroupInfos, nil
|
||||
}
|
||||
|
||||
func (c *Caller) UserRegisterCount(ctx context.Context, start int64, end int64) (map[string]int64, int64, error) {
|
||||
resp, err := registerUserCount.Call(ctx, c.imApi, &user.UserRegisterCountReq{
|
||||
Start: start,
|
||||
End: end,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return resp.Count, resp.Total, nil
|
||||
}
|
||||
|
||||
// OnlineUserCount 获取在线人数统计
|
||||
func (c *Caller) OnlineUserCount(ctx context.Context) (*OnlineUserCountResp, error) {
|
||||
return onlineUserCount.Call(ctx, c.imApi, &OnlineUserCountReq{})
|
||||
}
|
||||
|
||||
// OnlineUserCountTrend 获取在线人数走势统计
|
||||
func (c *Caller) OnlineUserCountTrend(ctx context.Context, req *OnlineUserCountTrendReq) (*OnlineUserCountTrendResp, error) {
|
||||
return onlineUserCountTrend.Call(ctx, c.imApi, req)
|
||||
}
|
||||
|
||||
// UserSendMsgCount 获取用户发送消息统计
|
||||
func (c *Caller) UserSendMsgCount(ctx context.Context, req *UserSendMsgCountReq) (*UserSendMsgCountResp, error) {
|
||||
return userSendMsgCount.Call(ctx, c.imApi, req)
|
||||
}
|
||||
|
||||
// UserSendMsgCountTrend 获取用户发送消息走势统计
|
||||
func (c *Caller) UserSendMsgCountTrend(ctx context.Context, req *UserSendMsgCountTrendReq) (*UserSendMsgCountTrendResp, error) {
|
||||
return userSendMsgCountTrend.Call(ctx, c.imApi, req)
|
||||
}
|
||||
|
||||
// UserSendMsgQuery 获取用户发送消息查询列表
|
||||
func (c *Caller) UserSendMsgQuery(ctx context.Context, req *UserSendMsgQueryReq) (*UserSendMsgQueryResp, error) {
|
||||
return userSendMsgQuery.Call(ctx, c.imApi, req)
|
||||
}
|
||||
|
||||
func (c *Caller) FriendUserIDs(ctx context.Context, userID string) ([]string, error) {
|
||||
resp, err := friendUserIDs.Call(ctx, c.imApi, &relation.GetFriendIDsReq{UserID: userID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.FriendIDs, nil
|
||||
}
|
||||
|
||||
// return true when isUserNotExist.
|
||||
func (c *Caller) AccountCheckSingle(ctx context.Context, userID string) (bool, error) {
|
||||
resp, err := accountCheck.Call(ctx, c.imApi, &user.AccountCheckReq{CheckUserIDs: []string{userID}})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if resp.Results[0].AccountStatus == constant.Registered {
|
||||
return false, eerrs.ErrAccountAlreadyRegister.Wrap()
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *Caller) SendSimpleMsg(ctx context.Context, req *SendSingleMsgReq, key string) error {
|
||||
_, err := sendSimpleMsg.CallWithQuery(ctx, c.imApi, req, map[string]string{botstruct.Key: key})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Caller) AddNotificationAccount(ctx context.Context, req *user.AddNotificationAccountReq) error {
|
||||
_, err := addNotificationAccount.Call(ctx, c.imApi, req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Caller) UpdateNotificationAccount(ctx context.Context, req *user.UpdateNotificationAccountInfoReq) error {
|
||||
_, err := updateNotificationAccount.Call(ctx, c.imApi, req)
|
||||
return err
|
||||
}
|
||||
139
pkg/common/imapi/model.go
Normal file
139
pkg/common/imapi/model.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package imapi
|
||||
|
||||
import "git.imall.cloud/openim/protocol/sdkws"
|
||||
|
||||
// SendSingleMsgReq defines the structure for sending a message to multiple recipients.
|
||||
type SendSingleMsgReq struct {
|
||||
// groupMsg should appoint sendID
|
||||
SendID string `json:"sendID"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"`
|
||||
Ex string `json:"ex"`
|
||||
}
|
||||
type SendSingleMsgResp struct{}
|
||||
|
||||
// OnlineUserCountReq 在线人数统计请求
|
||||
type OnlineUserCountReq struct{}
|
||||
|
||||
// OnlineUserCountResp 在线人数统计返回
|
||||
type OnlineUserCountResp struct {
|
||||
OnlineCount int64 `json:"onlineCount"`
|
||||
}
|
||||
|
||||
// OnlineUserCountTrendReq 在线人数走势统计请求
|
||||
type OnlineUserCountTrendReq struct {
|
||||
// StartTime 统计开始时间(毫秒时间戳),为空时默认最近24小时
|
||||
StartTime int64 `json:"startTime"`
|
||||
// EndTime 统计结束时间(毫秒时间戳),为空时默认当前时间
|
||||
EndTime int64 `json:"endTime"`
|
||||
// IntervalMinutes 统计间隔(分钟),仅支持15/30/60
|
||||
IntervalMinutes int32 `json:"intervalMinutes"`
|
||||
}
|
||||
|
||||
// OnlineUserCountTrendItem 在线人数走势数据点
|
||||
type OnlineUserCountTrendItem struct {
|
||||
// Timestamp 区间起始时间(毫秒时间戳)
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
// OnlineCount 区间内平均在线人数
|
||||
OnlineCount int64 `json:"onlineCount"`
|
||||
}
|
||||
|
||||
// OnlineUserCountTrendResp 在线人数走势统计返回
|
||||
type OnlineUserCountTrendResp struct {
|
||||
// IntervalMinutes 统计间隔(分钟)
|
||||
IntervalMinutes int32 `json:"intervalMinutes"`
|
||||
// Points 走势数据点
|
||||
Points []*OnlineUserCountTrendItem `json:"points"`
|
||||
}
|
||||
|
||||
// UserSendMsgCountReq 用户发送消息总数统计请求
|
||||
type UserSendMsgCountReq struct{}
|
||||
|
||||
// UserSendMsgCountResp 用户发送消息总数统计返回
|
||||
type UserSendMsgCountResp struct {
|
||||
// Count24h 最近24小时发送消息总数
|
||||
Count24h int64 `json:"count24h"`
|
||||
// Count7d 最近7天发送消息总数
|
||||
Count7d int64 `json:"count7d"`
|
||||
// Count30d 最近30天发送消息总数
|
||||
Count30d int64 `json:"count30d"`
|
||||
}
|
||||
|
||||
// UserSendMsgCountTrendReq 用户发送消息走势统计请求
|
||||
type UserSendMsgCountTrendReq struct {
|
||||
// UserID 发送者用户ID
|
||||
UserID string `json:"userID"`
|
||||
// ChatType 聊天类型:1-单聊,2-群聊
|
||||
ChatType int32 `json:"chatType"`
|
||||
// StartTime 统计开始时间(毫秒时间戳),为空时默认最近24小时
|
||||
StartTime int64 `json:"startTime"`
|
||||
// EndTime 统计结束时间(毫秒时间戳),为空时默认当前时间
|
||||
EndTime int64 `json:"endTime"`
|
||||
// IntervalMinutes 统计间隔(分钟),仅支持15/30/60
|
||||
IntervalMinutes int32 `json:"intervalMinutes"`
|
||||
}
|
||||
|
||||
// UserSendMsgCountTrendItem 用户发送消息走势数据点
|
||||
type UserSendMsgCountTrendItem struct {
|
||||
// Timestamp 区间起始时间(毫秒时间戳)
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
// Count 区间内发送消息数
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// UserSendMsgCountTrendResp 用户发送消息走势统计返回
|
||||
type UserSendMsgCountTrendResp struct {
|
||||
// UserID 发送者用户ID
|
||||
UserID string `json:"userID"`
|
||||
// ChatType 聊天类型:1-单聊,2-群聊
|
||||
ChatType int32 `json:"chatType"`
|
||||
// IntervalMinutes 统计间隔(分钟)
|
||||
IntervalMinutes int32 `json:"intervalMinutes"`
|
||||
// Points 走势数据点
|
||||
Points []*UserSendMsgCountTrendItem `json:"points"`
|
||||
}
|
||||
|
||||
// UserSendMsgQueryReq 用户发送消息查询请求
|
||||
type UserSendMsgQueryReq struct {
|
||||
UserID string `json:"userID"`
|
||||
StartTime int64 `json:"startTime"`
|
||||
EndTime int64 `json:"endTime"`
|
||||
Content string `json:"content"`
|
||||
PageNumber int32 `json:"pageNumber"`
|
||||
// ShowNumber 每页条数 默认50 最大200
|
||||
ShowNumber int32 `json:"showNumber"`
|
||||
}
|
||||
|
||||
// UserSendMsgQueryRecord 用户发送消息查询记录
|
||||
type UserSendMsgQueryRecord struct {
|
||||
// MsgID 使用服务端消息ID
|
||||
MsgID string `json:"msgID"`
|
||||
// SendID 发送者ID
|
||||
SendID string `json:"sendID"`
|
||||
// SenderName 发送者昵称或名称
|
||||
SenderName string `json:"senderName"`
|
||||
// RecvID 接收者ID 群聊为群ID
|
||||
RecvID string `json:"recvID"`
|
||||
// RecvName 接收者昵称或名称 群聊为群名称
|
||||
RecvName string `json:"recvName"`
|
||||
// ContentType 消息类型编号
|
||||
ContentType int32 `json:"contentType"`
|
||||
// ContentTypeName 消息类型名称
|
||||
ContentTypeName string `json:"contentTypeName"`
|
||||
// SessionType 聊天类型编号
|
||||
SessionType int32 `json:"sessionType"`
|
||||
// ChatTypeName 聊天类型名称
|
||||
ChatTypeName string `json:"chatTypeName"`
|
||||
// Content 消息内容
|
||||
Content string `json:"content"`
|
||||
// SendTime 消息发送时间
|
||||
SendTime int64 `json:"sendTime"`
|
||||
}
|
||||
|
||||
// UserSendMsgQueryResp 用户发送消息查询返回
|
||||
type UserSendMsgQueryResp struct {
|
||||
Count int64 `json:"count"`
|
||||
PageNumber int32 `json:"pageNumber"`
|
||||
ShowNumber int32 `json:"showNumber"`
|
||||
Records []*UserSendMsgQueryRecord `json:"records"`
|
||||
}
|
||||
48
pkg/common/imwebhook/message.go
Normal file
48
pkg/common/imwebhook/message.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package imwebhook
|
||||
|
||||
type CommonCallbackReq struct {
|
||||
SendID string `json:"sendID"`
|
||||
CallbackCommand string `json:"callbackCommand"`
|
||||
ServerMsgID string `json:"serverMsgID"`
|
||||
ClientMsgID string `json:"clientMsgID"`
|
||||
OperationID string `json:"operationID"`
|
||||
SenderPlatformID int32 `json:"senderPlatformID"`
|
||||
SenderNickname string `json:"senderNickname"`
|
||||
SessionType int32 `json:"sessionType"`
|
||||
MsgFrom int32 `json:"msgFrom"`
|
||||
ContentType int32 `json:"contentType"`
|
||||
Status int32 `json:"status"`
|
||||
SendTime int64 `json:"sendTime"`
|
||||
CreateTime int64 `json:"createTime"`
|
||||
Content string `json:"content"`
|
||||
Seq uint32 `json:"seq"`
|
||||
AtUserIDList []string `json:"atUserList"`
|
||||
SenderFaceURL string `json:"faceURL"`
|
||||
Ex string `json:"ex"`
|
||||
}
|
||||
|
||||
type CommonCallbackResp struct {
|
||||
ActionCode int32 `json:"actionCode"`
|
||||
ErrCode int32 `json:"errCode"`
|
||||
ErrMsg string `json:"errMsg"`
|
||||
ErrDlt string `json:"errDlt"`
|
||||
NextCode int32 `json:"nextCode"`
|
||||
}
|
||||
|
||||
type CallbackAfterSendSingleMsgReq struct {
|
||||
CommonCallbackReq
|
||||
RecvID string `json:"recvID"`
|
||||
}
|
||||
|
||||
type CallbackAfterSendSingleMsgResp struct {
|
||||
CommonCallbackResp
|
||||
}
|
||||
|
||||
type CallbackAfterSendGroupMsgReq struct {
|
||||
CommonCallbackReq
|
||||
GroupID string `json:"groupID"`
|
||||
}
|
||||
|
||||
type CallbackAfterSendGroupMsgResp struct {
|
||||
CommonCallbackResp
|
||||
}
|
||||
96
pkg/common/kdisc/direct/direct_resolver.go
Normal file
96
pkg/common/kdisc/direct/direct_resolver.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright © 2024 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 direct
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/openimsdk/tools/log"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
const (
|
||||
slashSeparator = "/"
|
||||
// EndpointSepChar is the separator char in endpoints.
|
||||
EndpointSepChar = ','
|
||||
|
||||
subsetSize = 32
|
||||
scheme = "direct"
|
||||
)
|
||||
|
||||
type ResolverDirect struct {
|
||||
}
|
||||
|
||||
func NewResolverDirect() *ResolverDirect {
|
||||
return &ResolverDirect{}
|
||||
}
|
||||
|
||||
func (rd *ResolverDirect) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (
|
||||
resolver.Resolver, error) {
|
||||
log.ZDebug(context.Background(), "Build", "target", target)
|
||||
endpoints := strings.FieldsFunc(GetEndpoints(target), func(r rune) bool {
|
||||
return r == EndpointSepChar
|
||||
})
|
||||
endpoints = subset(endpoints, subsetSize)
|
||||
addrs := make([]resolver.Address, 0, len(endpoints))
|
||||
|
||||
for _, val := range endpoints {
|
||||
addrs = append(addrs, resolver.Address{
|
||||
Addr: val,
|
||||
})
|
||||
}
|
||||
if err := cc.UpdateState(resolver.State{
|
||||
Addresses: addrs,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &nopResolver{cc: cc}, nil
|
||||
}
|
||||
func init() {
|
||||
resolver.Register(&ResolverDirect{})
|
||||
}
|
||||
func (rd *ResolverDirect) Scheme() string {
|
||||
return scheme // return your custom scheme name
|
||||
}
|
||||
|
||||
// GetEndpoints returns the endpoints from the given target.
|
||||
func GetEndpoints(target resolver.Target) string {
|
||||
return strings.Trim(target.URL.Path, slashSeparator)
|
||||
}
|
||||
func subset(set []string, sub int) []string {
|
||||
rand.Shuffle(len(set), func(i, j int) {
|
||||
set[i], set[j] = set[j], set[i]
|
||||
})
|
||||
if len(set) <= sub {
|
||||
return set
|
||||
}
|
||||
|
||||
return set[:sub]
|
||||
}
|
||||
|
||||
type nopResolver struct {
|
||||
cc resolver.ClientConn
|
||||
}
|
||||
|
||||
func (n nopResolver) ResolveNow(options resolver.ResolveNowOptions) {
|
||||
|
||||
}
|
||||
|
||||
func (n nopResolver) Close() {
|
||||
|
||||
}
|
||||
174
pkg/common/kdisc/direct/directconn.go
Normal file
174
pkg/common/kdisc/direct/directconn.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright © 2024 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 direct
|
||||
|
||||
//import (
|
||||
// "context"
|
||||
// "fmt"
|
||||
//
|
||||
// config2 "github.com/openimsdk/chat-deploy/v3/pkg/common/config"
|
||||
// "github.com/openimsdk/tools/errs"
|
||||
// "google.golang.org/grpc"
|
||||
// "google.golang.org/grpc/credentials/insecure"
|
||||
//)
|
||||
//
|
||||
//type ServiceAddresses map[string][]int
|
||||
//
|
||||
//func getServiceAddresses(rpcRegisterName *config2.RpcRegisterName,
|
||||
// rpcPort *config2.RpcPort, longConnSvrPort []int) ServiceAddresses {
|
||||
// return ServiceAddresses{
|
||||
// rpcRegisterName.OpenImUserName: rpcPort.OpenImUserPort,
|
||||
// rpcRegisterName.OpenImFriendName: rpcPort.OpenImFriendPort,
|
||||
// rpcRegisterName.OpenImMsgName: rpcPort.OpenImMessagePort,
|
||||
// rpcRegisterName.OpenImMessageGatewayName: longConnSvrPort,
|
||||
// rpcRegisterName.OpenImGroupName: rpcPort.OpenImGroupPort,
|
||||
// rpcRegisterName.OpenImAuthName: rpcPort.OpenImAuthPort,
|
||||
// rpcRegisterName.OpenImPushName: rpcPort.OpenImPushPort,
|
||||
// rpcRegisterName.OpenImConversationName: rpcPort.OpenImConversationPort,
|
||||
// rpcRegisterName.OpenImThirdName: rpcPort.OpenImThirdPort,
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//type ConnDirect struct {
|
||||
// additionalOpts []grpc.DialOption
|
||||
// currentServiceAddress string
|
||||
// conns map[string][]*grpc.ClientConn
|
||||
// resolverDirect *ResolverDirect
|
||||
// config *config2.GlobalConfig
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) GetClientLocalConns() map[string][]*grpc.ClientConn {
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) GetUserIdHashGatewayHost(ctx context.Context, userId string) (string, error) {
|
||||
// return "", nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) Register(serviceName, host string, port int, opts ...grpc.DialOption) error {
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) UnRegister() error {
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) CreateRpcRootNodes(serviceNames []string) error {
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) RegisterConf2Registry(key string, conf []byte) error {
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) GetConfFromRegistry(key string) ([]byte, error) {
|
||||
// return nil, nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) Close() {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func NewConnDirect(config *config2.GlobalConfig) (*ConnDirect, error) {
|
||||
// return &ConnDirect{
|
||||
// conns: make(map[string][]*grpc.ClientConn),
|
||||
// resolverDirect: NewResolverDirect(),
|
||||
// config: config,
|
||||
// }, nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) GetConns(ctx context.Context,
|
||||
// serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
|
||||
//
|
||||
// if conns, exists := cd.conns[serviceName]; exists {
|
||||
// return conns, nil
|
||||
// }
|
||||
// ports := getServiceAddresses(&cd.config.RpcRegisterName,
|
||||
// &cd.config.RpcPort, cd.config.LongConnSvr.OpenImMessageGatewayPort)[serviceName]
|
||||
// var connections []*grpc.ClientConn
|
||||
// for _, port := range ports {
|
||||
// conn, err := cd.dialServiceWithoutResolver(ctx, fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", port), append(cd.additionalOpts, opts...)...)
|
||||
// if err != nil {
|
||||
// return nil, errs.Wrap(fmt.Errorf("connect to port %d failed,serviceName %s, IP %s", port, serviceName, cd.config.Rpc.ListenIP))
|
||||
// }
|
||||
// connections = append(connections, conn)
|
||||
// }
|
||||
//
|
||||
// if len(connections) == 0 {
|
||||
// return nil, errs.New("no connections found for service", "serviceName", serviceName).Wrap()
|
||||
// }
|
||||
// return connections, nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
// // Get service addresses
|
||||
// addresses := getServiceAddresses(&cd.config.RpcRegisterName,
|
||||
// &cd.config.RpcPort, cd.config.LongConnSvr.OpenImMessageGatewayPort)
|
||||
// address, ok := addresses[serviceName]
|
||||
// if !ok {
|
||||
// return nil, errs.New("unknown service name", "serviceName", serviceName).Wrap()
|
||||
// }
|
||||
// var result string
|
||||
// for _, addr := range address {
|
||||
// if result != "" {
|
||||
// result = result + "," + fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", addr)
|
||||
// } else {
|
||||
// result = fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", addr)
|
||||
// }
|
||||
// }
|
||||
// // Try to dial a new connection
|
||||
// conn, err := cd.dialService(ctx, result, append(cd.additionalOpts, opts...)...)
|
||||
// if err != nil {
|
||||
// return nil, errs.WrapMsg(err, "address", result)
|
||||
// }
|
||||
//
|
||||
// // Store the new connection
|
||||
// cd.conns[serviceName] = append(cd.conns[serviceName], conn)
|
||||
// return conn, nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) GetSelfConnTarget() string {
|
||||
// return cd.currentServiceAddress
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) AddOption(opts ...grpc.DialOption) {
|
||||
// cd.additionalOpts = append(cd.additionalOpts, opts...)
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) CloseConn(conn *grpc.ClientConn) {
|
||||
// if conn != nil {
|
||||
// conn.Close()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) dialService(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
// options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
// conn, err := grpc.DialContext(ctx, cd.resolverDirect.Scheme()+":///"+address, options...)
|
||||
//
|
||||
// if err != nil {
|
||||
// return nil, errs.WrapMsg(err, "address", address)
|
||||
// }
|
||||
// return conn, nil
|
||||
//}
|
||||
//
|
||||
//func (cd *ConnDirect) dialServiceWithoutResolver(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
// options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
// conn, err := grpc.DialContext(ctx, address, options...)
|
||||
//
|
||||
// if err != nil {
|
||||
// return nil, errs.Wrap(err)
|
||||
// }
|
||||
// return conn, nil
|
||||
//}
|
||||
15
pkg/common/kdisc/direct/doc.go
Normal file
15
pkg/common/kdisc/direct/doc.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright © 2024 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 direct // import "github.com/openimsdk/chat-deploy/v3/pkg/common/discoveryregister/direct"
|
||||
51
pkg/common/kdisc/discoveryregister.go
Normal file
51
pkg/common/kdisc/discoveryregister.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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 kdisc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/discovery/etcd"
|
||||
"github.com/openimsdk/tools/discovery/kubernetes"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
const (
|
||||
ETCDCONST = "etcd"
|
||||
KUBERNETESCONST = "kubernetes"
|
||||
DIRECTCONST = "direct"
|
||||
)
|
||||
|
||||
// NewDiscoveryRegister creates a new service discovery and registry client based on the provided environment type.
|
||||
func NewDiscoveryRegister(discovery *config.Discovery, runtimeEnv string, watchNames []string) (discovery.SvcDiscoveryRegistry, error) {
|
||||
if runtimeEnv == KUBERNETESCONST {
|
||||
return kubernetes.NewKubernetesConnManager(discovery.Kubernetes.Namespace)
|
||||
}
|
||||
|
||||
switch discovery.Enable {
|
||||
case ETCDCONST:
|
||||
return etcd.NewSvcDiscoveryRegistry(
|
||||
discovery.Etcd.RootDirectory,
|
||||
discovery.Etcd.Address,
|
||||
watchNames,
|
||||
etcd.WithDialTimeout(10*time.Second),
|
||||
etcd.WithMaxCallSendMsgSize(20*1024*1024),
|
||||
etcd.WithUsernameAndPassword(discovery.Etcd.Username, discovery.Etcd.Password))
|
||||
default:
|
||||
return nil, errs.New("unsupported discovery type", "type", discovery.Enable).Wrap()
|
||||
}
|
||||
}
|
||||
106
pkg/common/kdisc/etcd/config_manager.go
Normal file
106
pkg/common/kdisc/etcd/config_manager.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
ShutDowns []func() error
|
||||
)
|
||||
|
||||
func RegisterShutDown(shutDown ...func() error) {
|
||||
ShutDowns = append(ShutDowns, shutDown...)
|
||||
}
|
||||
|
||||
type ConfigManager struct {
|
||||
client *clientv3.Client
|
||||
watchConfigNames []string
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func BuildKey(s string) string {
|
||||
return ConfigKeyPrefix + s
|
||||
}
|
||||
|
||||
func NewConfigManager(client *clientv3.Client, configNames []string) *ConfigManager {
|
||||
return &ConfigManager{
|
||||
client: client,
|
||||
watchConfigNames: datautil.Batch(func(s string) string { return BuildKey(s) }, append(configNames, RestartKey))}
|
||||
}
|
||||
|
||||
func (c *ConfigManager) Watch(ctx context.Context) {
|
||||
chans := make([]clientv3.WatchChan, 0, len(c.watchConfigNames))
|
||||
for _, name := range c.watchConfigNames {
|
||||
chans = append(chans, c.client.Watch(ctx, name, clientv3.WithPrefix()))
|
||||
}
|
||||
|
||||
doWatch := func(watchChan clientv3.WatchChan) {
|
||||
for watchResp := range watchChan {
|
||||
if watchResp.Err() != nil {
|
||||
log.ZError(ctx, "watch err", errs.Wrap(watchResp.Err()))
|
||||
continue
|
||||
}
|
||||
for _, event := range watchResp.Events {
|
||||
if event.IsModify() {
|
||||
if datautil.Contain(string(event.Kv.Key), c.watchConfigNames...) {
|
||||
c.lock.Lock()
|
||||
err := restartServer(ctx)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "restart server err", err)
|
||||
}
|
||||
c.lock.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, ch := range chans {
|
||||
go doWatch(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func restartServer(ctx context.Context) error {
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
return errs.New("get executable path fail").Wrap()
|
||||
}
|
||||
|
||||
args := os.Args
|
||||
env := os.Environ()
|
||||
|
||||
cmd := exec.Command(exePath, args[1:]...)
|
||||
cmd.Env = env
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
log.ZInfo(ctx, "shutdown server")
|
||||
for _, f := range ShutDowns {
|
||||
if err = f(); err != nil {
|
||||
log.ZError(ctx, "shutdown fail", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.ZInfo(ctx, "restart server")
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return errs.New("restart server fail").Wrap()
|
||||
}
|
||||
log.ZInfo(ctx, "cmd start over")
|
||||
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
9
pkg/common/kdisc/etcd/const.go
Normal file
9
pkg/common/kdisc/etcd/const.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package etcd
|
||||
|
||||
const (
|
||||
ConfigKeyPrefix = "/chat/config/"
|
||||
RestartKey = "restart"
|
||||
EnableConfigCenterKey = "enable-config-center"
|
||||
Enable = "enable"
|
||||
Disable = "disable"
|
||||
)
|
||||
136
pkg/common/mctx/get.go
Normal file
136
pkg/common/mctx/get.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 mctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
|
||||
constantpb "git.imall.cloud/openim/protocol/constant"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
"git.imall.cloud/openim/chat/pkg/common/tokenverify"
|
||||
)
|
||||
|
||||
func HaveOpUser(ctx context.Context) bool {
|
||||
return ctx.Value(constant.RpcOpUserID) != nil
|
||||
}
|
||||
|
||||
func Check(ctx context.Context) (string, int32, error) {
|
||||
opUserIDVal := ctx.Value(constant.RpcOpUserID)
|
||||
opUserID, ok := opUserIDVal.(string)
|
||||
if !ok {
|
||||
return "", 0, errs.ErrNoPermission.WrapMsg("no opUserID")
|
||||
}
|
||||
if opUserID == "" {
|
||||
return "", 0, errs.ErrNoPermission.WrapMsg("opUserID empty")
|
||||
}
|
||||
opUserTypeArr, ok := ctx.Value(constant.RpcOpUserType).([]string)
|
||||
if !ok {
|
||||
return "", 0, errs.ErrNoPermission.WrapMsg("missing user type")
|
||||
}
|
||||
if len(opUserTypeArr) == 0 {
|
||||
return "", 0, errs.ErrNoPermission.WrapMsg("user type empty")
|
||||
}
|
||||
userType, err := strconv.Atoi(opUserTypeArr[0])
|
||||
if err != nil {
|
||||
return "", 0, errs.ErrNoPermission.WrapMsg("user type invalid " + err.Error())
|
||||
}
|
||||
if !(userType == constant.AdminUser || userType == constant.NormalUser) {
|
||||
return "", 0, errs.ErrNoPermission.WrapMsg("user type invalid")
|
||||
}
|
||||
return opUserID, int32(userType), nil
|
||||
}
|
||||
|
||||
func CheckAdmin(ctx context.Context) (string, error) {
|
||||
userID, userType, err := Check(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if userType != constant.AdminUser {
|
||||
return "", errs.ErrNoPermission.WrapMsg("not admin")
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func CheckUser(ctx context.Context) (string, error) {
|
||||
userID, userType, err := Check(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if userType != constant.NormalUser {
|
||||
return "", errs.ErrNoPermission.WrapMsg("not user")
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func CheckAdminOrUser(ctx context.Context) (string, int32, error) {
|
||||
userID, userType, err := Check(ctx)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return userID, userType, nil
|
||||
}
|
||||
|
||||
func CheckAdminOr(ctx context.Context, userIDs ...string) error {
|
||||
userID, userType, err := Check(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if userType == tokenverify.TokenAdmin {
|
||||
return nil
|
||||
}
|
||||
for _, id := range userIDs {
|
||||
if userID == id {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errs.ErrNoPermission.WrapMsg("not admin or not in userIDs")
|
||||
}
|
||||
|
||||
func GetOpUserID(ctx context.Context) string {
|
||||
userID, _ := ctx.Value(constantpb.OpUserID).(string)
|
||||
return userID
|
||||
}
|
||||
|
||||
func GetUserType(ctx context.Context) (int, error) {
|
||||
userTypeArr, _ := ctx.Value(constant.RpcOpUserType).([]string)
|
||||
userType, err := strconv.Atoi(userTypeArr[0])
|
||||
if err != nil {
|
||||
return 0, errs.ErrNoPermission.WrapMsg("user type invalid " + err.Error())
|
||||
}
|
||||
return userType, nil
|
||||
}
|
||||
|
||||
func WithOpUserID(ctx context.Context, opUserID string, userType int) context.Context {
|
||||
headers, _ := ctx.Value(constant.RpcCustomHeader).([]string)
|
||||
ctx = context.WithValue(ctx, constant.RpcOpUserID, opUserID)
|
||||
ctx = context.WithValue(ctx, constant.RpcOpUserType, []string{strconv.Itoa(userType)})
|
||||
if datautil.IndexOf(constant.RpcOpUserType, headers...) < 0 {
|
||||
ctx = context.WithValue(ctx, constant.RpcCustomHeader, append(headers, constant.RpcOpUserType))
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func WithAdminUser(ctx context.Context, userID string) context.Context {
|
||||
return WithOpUserID(ctx, userID, constant.AdminUser)
|
||||
}
|
||||
|
||||
func WithApiToken(ctx context.Context, token string) context.Context {
|
||||
return context.WithValue(ctx, constant.CtxApiToken, token)
|
||||
}
|
||||
57
pkg/common/mw/gin_log.go
Normal file
57
pkg/common/mw/gin_log.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 mw
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
type responseWriter struct {
|
||||
gin.ResponseWriter
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func (w *responseWriter) Write(b []byte) (int, error) {
|
||||
w.buf.Write(b)
|
||||
return w.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
func GinLog() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
req, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
start := time.Now()
|
||||
log.ZDebug(c, "gin request", "method", c.Request.Method, "uri", c.Request.RequestURI, "req", string(req))
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader(req))
|
||||
writer := &responseWriter{
|
||||
ResponseWriter: c.Writer,
|
||||
buf: bytes.NewBuffer(nil),
|
||||
}
|
||||
c.Writer = writer
|
||||
c.Next()
|
||||
resp := writer.buf.Bytes()
|
||||
log.ZDebug(c, "gin response", "time", time.Since(start), "status", c.Writer.Status(), "resp", string(resp))
|
||||
}
|
||||
}
|
||||
36
pkg/common/mw/user.go
Normal file
36
pkg/common/mw/user.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 mw
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func AddUserType() grpc.DialOption {
|
||||
return grpc.WithChainUnaryInterceptor(func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
log.ZInfo(ctx, "add user type", "method", method)
|
||||
if arr, _ := ctx.Value(constant.RpcOpUserType).([]string); len(arr) > 0 {
|
||||
log.ZInfo(ctx, "add user type", "method", method, "userType", arr)
|
||||
headers, _ := ctx.Value(constant.RpcCustomHeader).([]string)
|
||||
ctx = context.WithValue(ctx, constant.RpcCustomHeader, append(headers, constant.RpcOpUserType))
|
||||
ctx = context.WithValue(ctx, constant.RpcOpUserType, arr)
|
||||
}
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
})
|
||||
}
|
||||
30
pkg/common/rtc/rtc.go
Normal file
30
pkg/common/rtc/rtc.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"github.com/livekit/protocol/auth"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewLiveKit(key, secret, url string) *LiveKit {
|
||||
return &LiveKit{
|
||||
token: auth.NewAccessToken(key, secret),
|
||||
url: url,
|
||||
}
|
||||
}
|
||||
|
||||
type LiveKit struct {
|
||||
token *auth.AccessToken
|
||||
url string
|
||||
}
|
||||
|
||||
func (l *LiveKit) GetLiveKitURL() string {
|
||||
return l.url
|
||||
}
|
||||
|
||||
func (l *LiveKit) GetLiveKitToken(room string, identity string) (string, error) {
|
||||
grant := &auth.VideoGrant{
|
||||
RoomJoin: true,
|
||||
Room: room,
|
||||
}
|
||||
return l.token.AddGrant(grant).SetIdentity(identity).SetValidFor(time.Hour).ToJWT()
|
||||
}
|
||||
129
pkg/common/startrpc/start.go
Normal file
129
pkg/common/startrpc/start.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package startrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"git.imall.cloud/openim/chat/pkg/common/kdisc"
|
||||
disetcd "git.imall.cloud/openim/chat/pkg/common/kdisc/etcd"
|
||||
"github.com/openimsdk/tools/discovery/etcd"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/runtimeenv"
|
||||
|
||||
"github.com/openimsdk/tools/discovery"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mw"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/openimsdk/tools/utils/network"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
// Start rpc server.
|
||||
func Start[T any](ctx context.Context, discovery *config.Discovery, listenIP,
|
||||
registerIP string, rpcPorts []int, index int, rpcRegisterName string, share *config.Share, config T,
|
||||
watchConfigNames []string, watchServiceNames []string,
|
||||
rpcFn func(ctx context.Context, config T, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error, options ...grpc.ServerOption) error {
|
||||
|
||||
runtimeEnv := runtimeenv.PrintRuntimeEnvironment()
|
||||
|
||||
rpcPort, err := datautil.GetElemByIndex(rpcPorts, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.CInfo(ctx, "RPC server is initializing", " runtimeEnv ", runtimeEnv, "rpcRegisterName", rpcRegisterName, "rpcPort", rpcPort)
|
||||
rpcTcpAddr := net.JoinHostPort(network.GetListenIP(listenIP), strconv.Itoa(rpcPort))
|
||||
listener, err := net.Listen(
|
||||
"tcp",
|
||||
rpcTcpAddr,
|
||||
)
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "listen err", "rpcTcpAddr", rpcTcpAddr)
|
||||
}
|
||||
|
||||
defer listener.Close()
|
||||
client, err := kdisc.NewDiscoveryRegister(discovery, runtimeEnv, watchServiceNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
registerIP, err = network.GetRpcRegisterIP(registerIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options = append(options, mw.GrpcServer())
|
||||
srv := grpc.NewServer(options...)
|
||||
once := sync.Once{}
|
||||
defer func() {
|
||||
once.Do(srv.GracefulStop)
|
||||
}()
|
||||
|
||||
err = rpcFn(ctx, config, client, srv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.Register(rpcRegisterName, registerIP, rpcPort, grpc.WithTransportCredentials(insecure.NewCredentials())); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
netDone = make(chan struct{}, 2)
|
||||
netErr error
|
||||
)
|
||||
|
||||
go func() {
|
||||
err := srv.Serve(listener)
|
||||
if err != nil {
|
||||
netErr = errs.WrapMsg(err, "rpc start err: ", rpcTcpAddr)
|
||||
netDone <- struct{}{}
|
||||
}
|
||||
}()
|
||||
if discovery.Enable == kdisc.ETCDCONST {
|
||||
cm := disetcd.NewConfigManager(client.(*etcd.SvcDiscoveryRegistryImpl).GetClient(), watchConfigNames)
|
||||
cm.Watch(ctx)
|
||||
}
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGTERM)
|
||||
select {
|
||||
case <-sigs:
|
||||
program.SIGTERMExit()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
if err := gracefulStopWithCtx(ctx, srv.GracefulStop); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
return nil
|
||||
case <-netDone:
|
||||
close(netDone)
|
||||
return netErr
|
||||
}
|
||||
}
|
||||
|
||||
func gracefulStopWithCtx(ctx context.Context, f func()) error {
|
||||
done := make(chan struct{}, 1)
|
||||
go func() {
|
||||
f()
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errs.New("timeout, ctx graceful stop")
|
||||
case <-done:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
132
pkg/common/tokenverify/token_verify.go
Normal file
132
pkg/common/tokenverify/token_verify.go
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 tokenverify
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/constant"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
const (
|
||||
TokenUser = constant.NormalUser
|
||||
TokenAdmin = constant.AdminUser
|
||||
)
|
||||
|
||||
type claims struct {
|
||||
UserID string
|
||||
UserType int32
|
||||
PlatformID int32
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
Expires time.Duration
|
||||
Secret string
|
||||
}
|
||||
|
||||
func (t *Token) secret() jwt.Keyfunc {
|
||||
return func(token *jwt.Token) (any, error) {
|
||||
return []byte(t.Secret), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Token) buildClaims(userID string, userType int32) claims {
|
||||
now := time.Now()
|
||||
return claims{
|
||||
UserID: userID,
|
||||
UserType: userType,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(t.Expires)), // Expiration time
|
||||
IssuedAt: jwt.NewNumericDate(now), // Issuing time
|
||||
NotBefore: jwt.NewNumericDate(now.Add(-time.Minute)), // Begin Effective time
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Token) getToken(str string) (string, int32, error) {
|
||||
token, err := jwt.ParseWithClaims(str, &claims{}, t.secret())
|
||||
if err != nil {
|
||||
if ve, ok := err.(*jwt.ValidationError); ok {
|
||||
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
|
||||
return "", 0, errs.ErrTokenMalformed.Wrap()
|
||||
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
|
||||
return "", 0, errs.ErrTokenExpired.Wrap()
|
||||
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
|
||||
return "", 0, errs.ErrTokenNotValidYet.Wrap()
|
||||
} else {
|
||||
return "", 0, errs.ErrTokenUnknown.Wrap()
|
||||
}
|
||||
} else {
|
||||
return "", 0, errs.ErrTokenNotValidYet.Wrap()
|
||||
}
|
||||
} else {
|
||||
claims, ok := token.Claims.(*claims)
|
||||
if claims.PlatformID != 0 {
|
||||
return "", 0, errs.ErrTokenExpired.Wrap()
|
||||
}
|
||||
if ok && token.Valid {
|
||||
return claims.UserID, claims.UserType, nil
|
||||
}
|
||||
return "", 0, errs.ErrTokenNotValidYet.Wrap()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Token) CreateToken(UserID string, userType int32) (string, time.Duration, error) {
|
||||
if !(userType == TokenUser || userType == TokenAdmin) {
|
||||
return "", 0, errs.ErrTokenUnknown.WrapMsg("token type unknown")
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, t.buildClaims(UserID, userType))
|
||||
str, err := token.SignedString([]byte(t.Secret))
|
||||
if err != nil {
|
||||
return "", 0, errs.Wrap(err)
|
||||
}
|
||||
return str, t.Expires, nil
|
||||
}
|
||||
|
||||
func (t *Token) GetToken(token string) (string, int32, error) {
|
||||
userID, userType, err := t.getToken(token)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if !(userType == TokenUser || userType == TokenAdmin) {
|
||||
return "", 0, errs.ErrTokenUnknown.WrapMsg("token type unknown")
|
||||
}
|
||||
return userID, userType, nil
|
||||
}
|
||||
|
||||
//func (t *Token) GetAdminTokenCache(token string) (string, error) {
|
||||
// userID, userType, err := getToken(token)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// if userType != TokenAdmin {
|
||||
// return "", errs.ErrTokenUnknown.WrapMsg("token type error")
|
||||
// }
|
||||
// return userID, nil
|
||||
//}
|
||||
//
|
||||
//func (t *Token) GetUserToken(token string) (string, error) {
|
||||
// userID, userType, err := getToken(token)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// if userType != TokenUser {
|
||||
// return "", errs.ErrTokenUnknown.WrapMsg("token type error")
|
||||
// }
|
||||
// return userID, nil
|
||||
//}
|
||||
248
pkg/common/util/aes.go
Normal file
248
pkg/common/util/aes.go
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 util
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/tools/errs"
|
||||
)
|
||||
|
||||
const (
|
||||
// AES密钥字符串
|
||||
aesKeyString = "F9cLjK3FgkvcR6vaWZKEj3DYJfwrIHMULLwpmDAj553mpmlvMYoLNIqajLnhIsWq"
|
||||
)
|
||||
|
||||
// DecryptVerifyCode 解密验证码
|
||||
// encryptedText: Base64编码的加密字符串
|
||||
// 返回: 解密后的明文(格式:验证码-时间戳)
|
||||
func DecryptVerifyCode(encryptedText string) (string, error) {
|
||||
// 解码Base64
|
||||
encrypted, err := base64.StdEncoding.DecodeString(encryptedText)
|
||||
if err != nil {
|
||||
return "", errs.WrapMsg(err, "base64解码失败")
|
||||
}
|
||||
|
||||
// 使用SHA-256哈希密钥字符串
|
||||
keyBytes := sha256.Sum256([]byte(aesKeyString))
|
||||
key := keyBytes[:]
|
||||
|
||||
// 创建AES cipher
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", errs.WrapMsg(err, "创建AES cipher失败")
|
||||
}
|
||||
|
||||
// 检查密文长度
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
return "", errs.New("密文长度不足")
|
||||
}
|
||||
|
||||
// 使用CBC模式,固定IV(全零)
|
||||
iv := make([]byte, aes.BlockSize) // 全零IV
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
|
||||
// 解密
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
mode.CryptBlocks(decrypted, encrypted)
|
||||
|
||||
// 去除PKCS7填充
|
||||
decrypted = unpadPKCS7(decrypted)
|
||||
|
||||
return string(decrypted), nil
|
||||
}
|
||||
|
||||
// unpadPKCS7 去除PKCS7填充
|
||||
func unpadPKCS7(data []byte) []byte {
|
||||
if len(data) == 0 {
|
||||
return data
|
||||
}
|
||||
padLen := int(data[len(data)-1])
|
||||
if padLen > len(data) || padLen == 0 {
|
||||
return data
|
||||
}
|
||||
// 验证填充
|
||||
for i := len(data) - padLen; i < len(data); i++ {
|
||||
if data[i] != byte(padLen) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
return data[:len(data)-padLen]
|
||||
}
|
||||
|
||||
// ParseVerifyCodeWithTimestamp 解析验证码和时间戳
|
||||
// format: "验证码-时间戳"
|
||||
// 返回: 验证码字符串和时间戳(毫秒)
|
||||
func ParseVerifyCodeWithTimestamp(decryptedText string) (string, int64, error) {
|
||||
var code string
|
||||
var timestamp int64
|
||||
|
||||
// 查找最后一个 "-" 分隔符
|
||||
lastDashIndex := -1
|
||||
for i := len(decryptedText) - 1; i >= 0; i-- {
|
||||
if decryptedText[i] == '-' {
|
||||
lastDashIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if lastDashIndex == -1 {
|
||||
return "", 0, errs.New("格式错误:未找到时间戳分隔符")
|
||||
}
|
||||
|
||||
code = decryptedText[:lastDashIndex]
|
||||
timestampStr := decryptedText[lastDashIndex+1:]
|
||||
|
||||
// 解析时间戳
|
||||
_, err := fmt.Sscanf(timestampStr, "%d", ×tamp)
|
||||
if err != nil {
|
||||
return "", 0, errs.WrapMsg(err, "解析时间戳失败")
|
||||
}
|
||||
|
||||
return code, timestamp, nil
|
||||
}
|
||||
|
||||
// ValidateTimestamp 验证时间戳是否在有效期内
|
||||
// timestamp: 毫秒级时间戳
|
||||
// maxAgeSeconds: 最大允许的秒数(允许的时间差,默认30秒)
|
||||
// 返回: 是否有效
|
||||
func ValidateTimestamp(timestamp int64, maxAgeSeconds int64) bool {
|
||||
if timestamp <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
currentTime := getCurrentTimestamp()
|
||||
diff := currentTime - timestamp
|
||||
|
||||
// 转换为毫秒
|
||||
maxAgeMillis := maxAgeSeconds * 1000
|
||||
|
||||
// 时间戳不能是未来的(允许5秒的时钟偏差)
|
||||
if diff < -5000 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 时间差不能超过maxAgeSeconds秒
|
||||
if diff > maxAgeMillis {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// getCurrentTimestamp 获取当前时间戳(毫秒)
|
||||
func getCurrentTimestamp() int64 {
|
||||
return time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
// padPKCS7 添加PKCS7填充
|
||||
func padPKCS7(data []byte, blockSize int) []byte {
|
||||
padLen := blockSize - len(data)%blockSize
|
||||
pad := make([]byte, padLen)
|
||||
for i := range pad {
|
||||
pad[i] = byte(padLen)
|
||||
}
|
||||
return append(data, pad...)
|
||||
}
|
||||
|
||||
// EncryptPhone 加密手机号
|
||||
// phoneNumber: 手机号明文
|
||||
// 返回: Base64编码的加密字符串
|
||||
func EncryptPhone(phoneNumber string) (string, error) {
|
||||
if phoneNumber == "" {
|
||||
return "", errs.New("手机号不能为空")
|
||||
}
|
||||
|
||||
// 使用SHA-256哈希密钥字符串
|
||||
keyBytes := sha256.Sum256([]byte(aesKeyString))
|
||||
key := keyBytes[:]
|
||||
|
||||
// 创建AES cipher
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", errs.WrapMsg(err, "创建AES cipher失败")
|
||||
}
|
||||
|
||||
// 使用CBC模式,固定IV(全零)
|
||||
iv := make([]byte, aes.BlockSize) // 全零IV
|
||||
|
||||
// 添加PKCS7填充
|
||||
plaintext := padPKCS7([]byte(phoneNumber), aes.BlockSize)
|
||||
|
||||
// 加密
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
encrypted := make([]byte, len(plaintext))
|
||||
mode.CryptBlocks(encrypted, plaintext)
|
||||
|
||||
// 编码为Base64
|
||||
return base64.StdEncoding.EncodeToString(encrypted), nil
|
||||
}
|
||||
|
||||
// DecryptPhone 解密手机号
|
||||
// encryptedText: Base64编码的加密字符串
|
||||
// 返回: 解密后的手机号明文
|
||||
func DecryptPhone(encryptedText string) (string, error) {
|
||||
// 复用DecryptVerifyCode的解密逻辑(手机号没有时间戳格式,直接解密)
|
||||
return DecryptVerifyCode(encryptedText)
|
||||
}
|
||||
|
||||
// EncryptRealNameAuthData 加密实名认证数据(使用 AES-GCM 模式)
|
||||
// data: JSON字符串,例如: {"cardNo":"330329199001021122","realName":"李四"}
|
||||
// key: AES密钥字符串(与服务端相同的默认密钥)
|
||||
// 返回: Base64编码的加密字符串
|
||||
func EncryptRealNameAuthData(data string, key string) (string, error) {
|
||||
if data == "" {
|
||||
return "", errs.New("数据不能为空")
|
||||
}
|
||||
if key == "" {
|
||||
return "", errs.New("密钥不能为空")
|
||||
}
|
||||
|
||||
// 使用 SHA256 哈希密钥字符串(与服务端保持一致)
|
||||
// 注意:直接对密钥字符串进行哈希,而不是先转换为字节
|
||||
hash := sha256.Sum256([]byte(key))
|
||||
aesKey := hash[:]
|
||||
|
||||
// 创建AES cipher
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return "", errs.WrapMsg(err, "创建AES cipher失败")
|
||||
}
|
||||
|
||||
// 使用 GCM 模式
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", errs.WrapMsg(err, "创建 GCM 失败")
|
||||
}
|
||||
|
||||
// 生成随机 nonce
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", errs.WrapMsg(err, "生成 nonce 失败")
|
||||
}
|
||||
|
||||
// 加密
|
||||
ciphertext := gcm.Seal(nonce, nonce, []byte(data), nil)
|
||||
|
||||
// 编码为Base64
|
||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
61
pkg/common/version/base.go
Normal file
61
pkg/common/version/base.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 version
|
||||
|
||||
// Base version information.
|
||||
//
|
||||
// This is the fallback data used when version information from git is not
|
||||
// provided via go ldflags. It provides an approximation of the Kubernetes
|
||||
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
|
||||
// information from git.
|
||||
//
|
||||
// If you are looking at these fields in the git tree, they look
|
||||
// strange. They are modified on the fly by the build process. The
|
||||
// in-tree values are dummy values used for "git archive", which also
|
||||
// works for GitHub tar downloads.
|
||||
//
|
||||
// When releasing a new Kubernetes version, this file is updated by
|
||||
// build/mark_new_version.sh to reflect the new version, and then a
|
||||
// git annotated tag (using format vX.Y where X == Major version and Y
|
||||
// == Minor version) is created to point to the commit that updates
|
||||
var (
|
||||
// TODO: Deprecate gitMajor and gitMinor, use only gitVersion
|
||||
// instead. First step in deprecation, keep the fields but make
|
||||
// them irrelevant. (Next we'll take it out, which may muck with
|
||||
// scripts consuming the kubectl version output - but most of
|
||||
// these should be looking at gitVersion already anyways.)
|
||||
gitMajor string = "" // major version, always numeric
|
||||
gitMinor string = "" // minor version, numeric possibly followed by "+"
|
||||
|
||||
// semantic version, derived by build scripts (see
|
||||
// https://github.com/kubernetes/sig-release/blob/master/release-engineering/versioning.md#kubernetes-release-versioning
|
||||
// https://kubernetes.io/releases/version-skew-policy/
|
||||
// for a detailed discussion of this field)
|
||||
//
|
||||
// TODO: This field is still called "gitVersion" for legacy
|
||||
// reasons. For prerelease versions, the build metadata on the
|
||||
// semantic version is a git hash, but the version itself is no
|
||||
// longer the direct output of "git describe", but a slight
|
||||
// translation to be semver compliant.
|
||||
|
||||
// NOTE: The $Format strings are replaced during 'git archive' thanks to the
|
||||
// companion .gitattributes file containing 'export-subst' in this same
|
||||
// directory. See also https://git-scm.com/docs/gitattributes
|
||||
gitVersion string = "latest"
|
||||
gitCommit string = "" // sha1 from git, output of $(git rev-parse HEAD)
|
||||
gitTreeState string = "" // state of git tree, either "clean" or "dirty"
|
||||
|
||||
buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
)
|
||||
34
pkg/common/version/types.go
Normal file
34
pkg/common/version/types.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 version
|
||||
|
||||
// Info contains versioning information.
|
||||
// TODO: Add []string of api versions supported? It's still unclear
|
||||
// how we'll want to distribute that information.
|
||||
type Info struct {
|
||||
Major string `json:"major,omitempty"`
|
||||
Minor string `json:"minor,omitempty"`
|
||||
GitVersion string `json:"gitVersion"`
|
||||
GitCommit string `json:"gitCommit,omitempty"`
|
||||
BuildDate string `json:"buildDate"`
|
||||
GoVersion string `json:"goVersion"`
|
||||
Compiler string `json:"compiler"`
|
||||
Platform string `json:"platform"`
|
||||
}
|
||||
|
||||
// String returns info as a human-friendly version string.
|
||||
func (info Info) String() string {
|
||||
return info.GitVersion
|
||||
}
|
||||
52
pkg/common/version/version.go
Normal file
52
pkg/common/version/version.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright © 2023 OpenIM open source community. 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 version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Get returns the overall codebase version. It's for detecting
|
||||
// what code a binary was built from.
|
||||
func Get() Info {
|
||||
// These variables typically come from -ldflags settings and in
|
||||
// their absence fallback to the settings in ./base.go
|
||||
return Info{
|
||||
Major: gitMajor,
|
||||
Minor: gitMinor,
|
||||
GitVersion: gitVersion,
|
||||
GitCommit: gitCommit,
|
||||
BuildDate: buildDate,
|
||||
GoVersion: runtime.Version(),
|
||||
Compiler: runtime.Compiler,
|
||||
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
}
|
||||
|
||||
// GetSingleVersion returns single version of sealer
|
||||
func GetSingleVersion() string {
|
||||
return gitVersion
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
OpenIMChatVersion Info `json:"OpenIMChatVersion,omitempty" yaml:"OpenIMChatVersion,omitempty"`
|
||||
OpenIMServerVersion *OpenIMServerVersion `json:"OpenIMServerVersion,omitempty" yaml:"OpenIMServerVersion,omitempty"`
|
||||
}
|
||||
|
||||
type OpenIMServerVersion struct {
|
||||
ServerVersion string `json:"serverVersion,omitempty" yaml:"serverVersion,omitempty"`
|
||||
ClientVersion string `json:"clientVersion,omitempty" yaml:"clientVersion,omitempty"` //sdk core version
|
||||
}
|
||||
115
pkg/common/xlsx/main.go
Normal file
115
pkg/common/xlsx/main.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package xlsx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func ParseSheet(file *excelize.File, v interface{}) error {
|
||||
val := reflect.ValueOf(v)
|
||||
if val.Kind() != reflect.Ptr {
|
||||
return errors.New("not ptr")
|
||||
}
|
||||
val = val.Elem()
|
||||
if val.Kind() != reflect.Slice {
|
||||
return errors.New("not slice")
|
||||
}
|
||||
itemType := val.Type().Elem()
|
||||
if itemType.Kind() != reflect.Struct {
|
||||
return errors.New("not struct")
|
||||
}
|
||||
newItemValue := func() reflect.Value {
|
||||
return reflect.New(itemType).Elem()
|
||||
}
|
||||
putItem := func(v reflect.Value) {
|
||||
val.Set(reflect.Append(val, v))
|
||||
}
|
||||
var sheetName string
|
||||
if s, ok := newItemValue().Interface().(SheetName); ok {
|
||||
sheetName = s.SheetName()
|
||||
} else {
|
||||
sheetName = itemType.Name()
|
||||
}
|
||||
|
||||
if sheetIndex, err := file.GetSheetIndex(sheetName); err != nil {
|
||||
return err
|
||||
} else if sheetIndex < 0 {
|
||||
return nil
|
||||
}
|
||||
fieldIndex := make(map[string]int)
|
||||
for i := 0; i < itemType.NumField(); i++ {
|
||||
field := itemType.Field(i)
|
||||
alias := field.Tag.Get("column")
|
||||
switch alias {
|
||||
case "":
|
||||
fieldIndex[field.Name] = i
|
||||
case "-":
|
||||
continue
|
||||
default:
|
||||
fieldIndex[alias] = i
|
||||
}
|
||||
}
|
||||
if len(fieldIndex) == 0 {
|
||||
return errors.New("empty column struct")
|
||||
}
|
||||
sheetIndex := make(map[string]int)
|
||||
for i := 1; ; i++ {
|
||||
name, err := file.GetCellValue(sheetName, GetAxis(i, 1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if name == "" {
|
||||
break
|
||||
}
|
||||
if _, ok := fieldIndex[name]; ok {
|
||||
sheetIndex[name] = i
|
||||
}
|
||||
}
|
||||
if len(sheetIndex) == 0 {
|
||||
return errors.New("sheet column empty")
|
||||
}
|
||||
for i := 2; ; i++ {
|
||||
var (
|
||||
notEmpty int
|
||||
item = newItemValue()
|
||||
)
|
||||
for column, index := range sheetIndex {
|
||||
s, err := file.GetCellValue(sheetName, GetAxis(index, i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
notEmpty++
|
||||
if err = String2Value(s, item.Field(fieldIndex[column])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if notEmpty > 0 {
|
||||
putItem(item)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseAll(r io.Reader, models ...interface{}) error {
|
||||
if len(models) == 0 {
|
||||
return errors.New("empty models")
|
||||
}
|
||||
file, err := excelize.OpenReader(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
for i := 0; i < len(models); i++ {
|
||||
if err := ParseSheet(file, models[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user