Files
wehub-resource-sync 39dbe3a57d
CI / Check and lint (push) Failing after 1s
Lint / Lint (ubuntu-latest) (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:22:32 +08:00

250 lines
8.1 KiB
Go

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 report
import (
"context"
"encoding/json"
"github.com/apache/answer/internal/service/eventqueue"
"github.com/apache/answer/internal/base/constant"
"github.com/apache/answer/internal/base/handler"
"github.com/apache/answer/internal/base/pager"
"github.com/apache/answer/internal/base/reason"
"github.com/apache/answer/internal/entity"
"github.com/apache/answer/internal/schema"
answercommon "github.com/apache/answer/internal/service/answer_common"
"github.com/apache/answer/internal/service/comment_common"
"github.com/apache/answer/internal/service/config"
"github.com/apache/answer/internal/service/object_info"
questioncommon "github.com/apache/answer/internal/service/question_common"
"github.com/apache/answer/internal/service/report_common"
"github.com/apache/answer/internal/service/report_handle"
usercommon "github.com/apache/answer/internal/service/user_common"
"github.com/apache/answer/pkg/checker"
"github.com/apache/answer/pkg/htmltext"
"github.com/apache/answer/pkg/obj"
"github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
)
// ReportService user service
type ReportService struct {
reportRepo report_common.ReportRepo
objectInfoService *object_info.ObjService
commonUser *usercommon.UserCommon
answerRepo answercommon.AnswerRepo
questionRepo questioncommon.QuestionRepo
commentCommonRepo comment_common.CommentCommonRepo
reportHandle *report_handle.ReportHandle
configService *config.ConfigService
eventQueueService eventqueue.Service
}
// NewReportService new report service
func NewReportService(
reportRepo report_common.ReportRepo,
objectInfoService *object_info.ObjService,
commonUser *usercommon.UserCommon,
answerRepo answercommon.AnswerRepo,
questionRepo questioncommon.QuestionRepo,
commentCommonRepo comment_common.CommentCommonRepo,
reportHandle *report_handle.ReportHandle,
configService *config.ConfigService,
eventQueueService eventqueue.Service,
) *ReportService {
return &ReportService{
reportRepo: reportRepo,
objectInfoService: objectInfoService,
commonUser: commonUser,
answerRepo: answerRepo,
questionRepo: questionRepo,
commentCommonRepo: commentCommonRepo,
reportHandle: reportHandle,
configService: configService,
eventQueueService: eventQueueService,
}
}
// AddReport add report
func (rs *ReportService) AddReport(ctx context.Context, req *schema.AddReportReq) (err error) {
objectTypeNumber, err := obj.GetObjectTypeNumberByObjectID(req.ObjectID)
if err != nil {
return err
}
objInfo, err := rs.objectInfoService.GetInfo(ctx, req.ObjectID)
if err != nil {
return err
}
if objInfo.IsDeleted() {
return errors.BadRequest(reason.NewObjectAlreadyDeleted)
}
cf, err := rs.configService.GetConfigByID(ctx, req.ReportType)
if err != nil || cf == nil {
return errors.BadRequest(reason.ReportNotFound)
}
if cf.Key == constant.ReasonADuplicate && !checker.IsURL(req.Content) {
return errors.BadRequest(reason.InvalidURLError)
}
report := &entity.Report{
UserID: req.UserID,
ReportedUserID: objInfo.ObjectCreatorUserID,
ObjectID: req.ObjectID,
ObjectType: objectTypeNumber,
ReportType: req.ReportType,
Content: req.Content,
Status: entity.ReportStatusPending,
}
err = rs.reportRepo.AddReport(ctx, report)
if err != nil {
return err
}
rs.sendEvent(ctx, report, objInfo)
return nil
}
// GetUnreviewedReportPostPage get unreviewed report post page
func (rs *ReportService) GetUnreviewedReportPostPage(ctx context.Context, req *schema.GetUnreviewedReportPostPageReq) (
pageModel *pager.PageModel, err error) {
if !req.IsAdmin {
return pager.NewPageModel(0, make([]*schema.GetReportListPageResp, 0)), nil
}
lang := handler.GetLangByCtx(ctx)
reports, total, err := rs.reportRepo.GetReportListPage(ctx, &schema.GetReportListPageDTO{
Page: req.Page,
PageSize: 1,
Status: entity.ReportStatusPending,
})
if err != nil {
return
}
resp := make([]*schema.GetReportListPageResp, 0)
for _, report := range reports {
info, err := rs.objectInfoService.GetUnreviewedRevisionInfo(ctx, report.ObjectID)
if err != nil {
log.Errorf("GetUnreviewedRevisionInfo failed, err: %v", err)
continue
}
r := &schema.GetReportListPageResp{
FlagID: report.ID,
CreatedAt: info.CreatedAt,
ObjectID: info.ObjectID,
ObjectType: info.ObjectType,
QuestionID: info.QuestionID,
AnswerID: info.AnswerID,
CommentID: info.CommentID,
Title: info.Title,
UrlTitle: htmltext.UrlTitle(info.Title),
OriginalText: info.Content,
ParsedText: info.Html,
AnswerCount: info.AnswerCount,
AnswerAccepted: info.AnswerAccepted,
Tags: info.Tags,
SubmitAt: report.CreatedAt.Unix(),
ObjectStatus: info.Status,
ObjectShowStatus: info.ShowStatus,
ReasonContent: report.Content,
}
// get user info
userInfo, exists, e := rs.commonUser.GetUserBasicInfoByID(ctx, info.ObjectCreatorUserID)
if e != nil {
log.Errorf("user not found by id: %s, err: %v", info.ObjectCreatorUserID, e)
}
if exists {
_ = copier.Copy(&r.AuthorUserInfo, userInfo)
}
// get submitter info
submitter, exists, e := rs.commonUser.GetUserBasicInfoByID(ctx, report.ReportedUserID)
if e != nil {
log.Errorf("user not found by id: %s, err: %v", info.ObjectCreatorUserID, e)
}
if exists {
_ = copier.Copy(&r.SubmitterUser, submitter)
}
if report.ReportType > 0 {
r.Reason = &schema.ReasonItem{ReasonType: report.ReportType}
cf, err := rs.configService.GetConfigByID(ctx, report.ReportType)
if err != nil {
log.Error(err)
} else {
_ = json.Unmarshal([]byte(cf.Value), r.Reason)
r.Reason.Translate(cf.Key, lang)
}
}
resp = append(resp, r)
}
return pager.NewPageModel(total, resp), nil
}
// ReviewReport review report
func (rs *ReportService) ReviewReport(ctx context.Context, req *schema.ReviewReportReq) (err error) {
report, exist, err := rs.reportRepo.GetByID(ctx, req.FlagID)
if err != nil {
return err
}
if !exist {
return errors.NotFound(reason.ReportNotFound)
}
// check if handle or not
if report.Status != entity.ReportStatusPending {
return nil
}
// ignore this report
if req.OperationType == constant.ReportOperationIgnoreReport {
return rs.reportRepo.UpdateStatus(ctx, report.ID, entity.ReportStatusIgnore)
}
if err = rs.reportHandle.UpdateReportedObject(ctx, report, req); err != nil {
return
}
return rs.reportRepo.UpdateStatus(ctx, report.ID, entity.ReportStatusCompleted)
}
func (rs *ReportService) sendEvent(ctx context.Context,
report *entity.Report, objectInfo *schema.SimpleObjectInfo) {
var event *schema.EventMsg
switch objectInfo.ObjectType {
case constant.QuestionObjectType:
event = schema.NewEvent(constant.EventQuestionFlag, report.UserID).TID(objectInfo.QuestionID).
QID(objectInfo.QuestionID, objectInfo.ObjectCreatorUserID)
case constant.AnswerObjectType:
event = schema.NewEvent(constant.EventAnswerFlag, report.UserID).TID(objectInfo.AnswerID).
AID(objectInfo.AnswerID, objectInfo.ObjectCreatorUserID)
case constant.CommentObjectType:
event = schema.NewEvent(constant.EventCommentFlag, report.UserID).TID(objectInfo.CommentID).
CID(objectInfo.CommentID, objectInfo.ObjectCreatorUserID)
default:
return
}
rs.eventQueueService.Send(ctx, event)
}