parent
a3bda263a7
commit
7e65b21c5e
@ -0,0 +1,143 @@
|
||||
// Copyright © 2024 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
)
|
||||
|
||||
func genReportID() string {
|
||||
const dataLen = 12
|
||||
data := make([]byte, dataLen)
|
||||
rand.Read(data)
|
||||
chars := []byte("0123456789abcdefghijklmnopqrstuvwxyz")
|
||||
for i := 0; i < len(data); i++ {
|
||||
data[i] = chars[data[i]%byte(len(chars))]
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (m *msgServer) ReportSpam(ctx context.Context, req *msg.ReportSpamReq) (*msg.ReportSpamResp, error) {
|
||||
if req.ReportedUserID == "" {
|
||||
return nil, errs.ErrArgs.WrapMsg("reportedUserID is required")
|
||||
}
|
||||
if req.ReasonType <= 0 {
|
||||
return nil, errs.ErrArgs.WrapMsg("reasonType must be positive")
|
||||
}
|
||||
|
||||
reporterUserID := mcontext.GetOpUserID(ctx)
|
||||
|
||||
report := &model.SpamReport{
|
||||
ReporterUserID: reporterUserID,
|
||||
ReportedUserID: req.ReportedUserID,
|
||||
ConversationID: req.ConversationID,
|
||||
ClientMsgID: req.ClientMsgID,
|
||||
Seq: req.Seq,
|
||||
ReasonType: req.ReasonType,
|
||||
Reason: req.Reason,
|
||||
Status: model.SpamReportStatusPending,
|
||||
CreateTime: time.Now(),
|
||||
Ex: req.Ex,
|
||||
}
|
||||
|
||||
// Generate a unique reportID.
|
||||
for i := 0; i < 20; i++ {
|
||||
id := genReportID()
|
||||
existing, err := m.spamReportDB.Get(ctx, id)
|
||||
if err == nil && existing != nil {
|
||||
continue
|
||||
}
|
||||
report.ReportID = id
|
||||
break
|
||||
}
|
||||
if report.ReportID == "" {
|
||||
return nil, errs.ErrInternalServer.WrapMsg("failed to generate report ID")
|
||||
}
|
||||
|
||||
if err := m.spamReportDB.Create(ctx, report); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &msg.ReportSpamResp{ReportID: report.ReportID}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) GetSpamReports(ctx context.Context, req *msg.GetSpamReportsReq) (*msg.GetSpamReportsResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var start, end time.Time
|
||||
if req.StartTime > 0 {
|
||||
start = time.UnixMilli(req.StartTime)
|
||||
}
|
||||
if req.EndTime > 0 {
|
||||
end = time.UnixMilli(req.EndTime)
|
||||
}
|
||||
|
||||
total, reports, err := m.spamReportDB.Find(ctx, req.Status, req.ReportedUserID, req.ReporterUserID,
|
||||
start, end, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pbReports := datautil.Slice(reports, func(r *model.SpamReport) *msg.SpamReportInfo {
|
||||
return &msg.SpamReportInfo{
|
||||
ReportID: r.ReportID,
|
||||
ReporterUserID: r.ReporterUserID,
|
||||
ReportedUserID: r.ReportedUserID,
|
||||
ConversationID: r.ConversationID,
|
||||
ClientMsgID: r.ClientMsgID,
|
||||
Seq: r.Seq,
|
||||
ReasonType: r.ReasonType,
|
||||
Reason: r.Reason,
|
||||
Status: r.Status,
|
||||
CreateTime: r.CreateTime.UnixMilli(),
|
||||
HandleTime: r.HandleTime.UnixMilli(),
|
||||
HandlerUserID: r.HandlerUserID,
|
||||
Ex: r.Ex,
|
||||
}
|
||||
})
|
||||
|
||||
return &msg.GetSpamReportsResp{
|
||||
Reports: pbReports,
|
||||
Total: uint32(total),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) HandleSpamReport(ctx context.Context, req *msg.HandleSpamReportReq) (*msg.HandleSpamReportResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.ReportID == "" {
|
||||
return nil, errs.ErrArgs.WrapMsg("reportID is required")
|
||||
}
|
||||
if req.Status != model.SpamReportStatusHandled && req.Status != model.SpamReportStatusIgnored {
|
||||
return nil, errs.ErrArgs.WrapMsg("status must be 1 (handled) or 2 (ignored)")
|
||||
}
|
||||
|
||||
handlerUserID := mcontext.GetOpUserID(ctx)
|
||||
if err := m.spamReportDB.UpdateStatus(ctx, req.ReportID, req.Status, handlerUserID, time.Now()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &msg.HandleSpamReportResp{}, nil
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
// Copyright © 2024 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mgo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewSpamReportMongo(db *mongo.Database) (database.SpamReport, error) {
|
||||
coll := db.Collection(database.SpamReportName)
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{{Key: "report_id", Value: 1}},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "reporter_user_id", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "reported_user_id", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "status", Value: 1},
|
||||
{Key: "create_time", Value: -1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SpamReportMgo{coll: coll}, nil
|
||||
}
|
||||
|
||||
type SpamReportMgo struct {
|
||||
coll *mongo.Collection
|
||||
}
|
||||
|
||||
func (s *SpamReportMgo) Create(ctx context.Context, report *model.SpamReport) error {
|
||||
return mongoutil.InsertOne(ctx, s.coll, report)
|
||||
}
|
||||
|
||||
func (s *SpamReportMgo) Find(ctx context.Context, status int32, reportedUserID, reporterUserID string,
|
||||
start, end time.Time, pagination pagination.Pagination) (int64, []*model.SpamReport, error) {
|
||||
filter := bson.M{}
|
||||
if status >= 0 {
|
||||
filter["status"] = status
|
||||
}
|
||||
if reportedUserID != "" {
|
||||
filter["reported_user_id"] = reportedUserID
|
||||
}
|
||||
if reporterUserID != "" {
|
||||
filter["reporter_user_id"] = reporterUserID
|
||||
}
|
||||
if !start.IsZero() || !end.IsZero() {
|
||||
timeFilter := bson.M{}
|
||||
if !start.IsZero() {
|
||||
timeFilter["$gte"] = start
|
||||
}
|
||||
if !end.IsZero() {
|
||||
timeFilter["$lte"] = end
|
||||
}
|
||||
filter["create_time"] = timeFilter
|
||||
}
|
||||
return mongoutil.FindPage[*model.SpamReport](ctx, s.coll, filter, pagination,
|
||||
options.Find().SetSort(bson.D{{Key: "create_time", Value: -1}}))
|
||||
}
|
||||
|
||||
func (s *SpamReportMgo) UpdateStatus(ctx context.Context, reportID string, status int32, handlerUserID string, handleTime time.Time) error {
|
||||
return mongoutil.UpdateOne(ctx, s.coll,
|
||||
bson.M{"report_id": reportID},
|
||||
bson.M{"$set": bson.M{
|
||||
"status": status,
|
||||
"handler_user_id": handlerUserID,
|
||||
"handle_time": handleTime,
|
||||
}},
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SpamReportMgo) Get(ctx context.Context, reportID string) (*model.SpamReport, error) {
|
||||
return mongoutil.FindOne[*model.SpamReport](ctx, s.coll, bson.M{"report_id": reportID})
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
// Copyright © 2024 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
|
||||
type SpamReport interface {
|
||||
// Create inserts a new spam report record.
|
||||
Create(ctx context.Context, report *model.SpamReport) error
|
||||
// Find queries spam reports with optional filters, returns total count and records.
|
||||
Find(ctx context.Context, status int32, reportedUserID, reporterUserID string,
|
||||
start, end time.Time, pagination pagination.Pagination) (int64, []*model.SpamReport, error)
|
||||
// UpdateStatus updates the handling status of a spam report.
|
||||
UpdateStatus(ctx context.Context, reportID string, status int32, handlerUserID string, handleTime time.Time) error
|
||||
// Get retrieves a single spam report by its reportID.
|
||||
Get(ctx context.Context, reportID string) (*model.SpamReport, error)
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
// Copyright © 2024 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// SpamReport status constants.
|
||||
const (
|
||||
SpamReportStatusPending int32 = 0 // 待处理
|
||||
SpamReportStatusHandled int32 = 1 // 已处理
|
||||
SpamReportStatusIgnored int32 = 2 // 已忽略
|
||||
)
|
||||
|
||||
// SpamReport reason type constants.
|
||||
const (
|
||||
SpamReasonTypeSpam int32 = 1 // 垃圾消息
|
||||
SpamReasonTypePorn int32 = 2 // 色情内容
|
||||
SpamReasonTypeIllegal int32 = 3 // 违法内容
|
||||
SpamReasonTypeOther int32 = 4 // 其他
|
||||
)
|
||||
|
||||
type SpamReport struct {
|
||||
ID primitive.ObjectID `bson:"_id"`
|
||||
ReportID string `bson:"report_id"`
|
||||
ReporterUserID string `bson:"reporter_user_id"`
|
||||
ReportedUserID string `bson:"reported_user_id"`
|
||||
ConversationID string `bson:"conversation_id"` // 举报具体消息时填写
|
||||
ClientMsgID string `bson:"client_msg_id"` // 举报具体消息时填写
|
||||
Seq int64 `bson:"seq"`
|
||||
ReasonType int32 `bson:"reason_type"` // 1垃圾 2色情 3违法 4其他
|
||||
Reason string `bson:"reason"`
|
||||
Status int32 `bson:"status"` // 0待处理 1已处理 2已忽略
|
||||
CreateTime time.Time `bson:"create_time"`
|
||||
HandleTime time.Time `bson:"handle_time"`
|
||||
HandlerUserID string `bson:"handler_user_id"`
|
||||
Ex string `bson:"ex"`
|
||||
}
|
||||
Loading…
Reference in new issue