复制项目

This commit is contained in:
kim.dev.6789
2026-01-14 22:35:45 +08:00
parent 305d526110
commit b7f8db7d08
297 changed files with 81784 additions and 0 deletions

50
pkg/common/db/cache/imtoken.go vendored Normal file
View 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
View 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
View 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
}

View 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)
}

View 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)
}

View 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)
}

View 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
}

View 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 操作符的值可以是任意值通常使用空字符串或1MongoDB 会忽略值,只删除字段
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)
}

View 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}})
}

View 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}))
}

View 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
}

View 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}))
}

View 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)
}

View 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}})
}

View 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}
}

View 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{})
}

View 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)
}

View 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)
}

View 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})
}

View 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}})
}

View 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}})
}

View 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)
}

View 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}}))
}

View 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}})
}

View 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)
}

View 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
}

View 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}}))
}

View 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}}))
}

View 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})
}

View 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{ // 身份证长度至少 6null/非字符串时按空字符串处理,避免 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{ // 身份证长度至少 6null/非字符串时按空字符串处理,避免 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
}

View 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})
}

View 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}}))
}

View 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)
}

View 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)
}

View 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)
}

View 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)
}

View 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
}

View 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)
}

View 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)
}

View 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
}

View 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)
}

View 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) // 统计好友总数
}

View 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) // 统计今天新建的群组数
}

View 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)
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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"` // 收藏IDMongoDB 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)
}

View 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 // 默认优先级
)

View 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
}

View 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
}

View 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)
}

View 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 的配置
}

View 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查询
}

View 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
}

View 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)
}

View 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)
}

View 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
}