复制项目

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

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