| /* |
| * 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 review |
| |
| import ( |
| "context" |
| |
| "github.com/apache/answer/internal/base/constant" |
| "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" |
| commentcommon "github.com/apache/answer/internal/service/comment_common" |
| "github.com/apache/answer/internal/service/notice_queue" |
| "github.com/apache/answer/internal/service/object_info" |
| questioncommon "github.com/apache/answer/internal/service/question_common" |
| "github.com/apache/answer/internal/service/role" |
| "github.com/apache/answer/internal/service/siteinfo_common" |
| tagcommon "github.com/apache/answer/internal/service/tag_common" |
| usercommon "github.com/apache/answer/internal/service/user_common" |
| "github.com/apache/answer/pkg/htmltext" |
| "github.com/apache/answer/pkg/token" |
| "github.com/apache/answer/pkg/uid" |
| "github.com/apache/answer/plugin" |
| "github.com/jinzhu/copier" |
| "github.com/segmentfault/pacman/errors" |
| "github.com/segmentfault/pacman/log" |
| ) |
| |
| // ReviewRepo review repository |
| type ReviewRepo interface { |
| AddReview(ctx context.Context, review *entity.Review) (err error) |
| UpdateReviewStatus(ctx context.Context, reviewID int, reviewerUserID string, status int) (err error) |
| GetReview(ctx context.Context, reviewID int) (review *entity.Review, exist bool, err error) |
| GetReviewByObject(ctx context.Context, objectID string) (review *entity.Review, exist bool, err error) |
| GetReviewCount(ctx context.Context, status int) (count int64, err error) |
| GetReviewPage(ctx context.Context, page, pageSize int, cond *entity.Review) (reviewList []*entity.Review, total int64, err error) |
| } |
| |
| // ReviewService user service |
| type ReviewService struct { |
| reviewRepo ReviewRepo |
| objectInfoService *object_info.ObjService |
| userCommon *usercommon.UserCommon |
| userRepo usercommon.UserRepo |
| questionRepo questioncommon.QuestionRepo |
| answerRepo answercommon.AnswerRepo |
| userRoleService *role.UserRoleRelService |
| tagCommon *tagcommon.TagCommonService |
| questionCommon *questioncommon.QuestionCommon |
| externalNotificationQueueService notice_queue.ExternalNotificationQueueService |
| notificationQueueService notice_queue.NotificationQueueService |
| siteInfoService siteinfo_common.SiteInfoCommonService |
| commentCommonRepo commentcommon.CommentCommonRepo |
| } |
| |
| // NewReviewService new review service |
| func NewReviewService( |
| reviewRepo ReviewRepo, |
| objectInfoService *object_info.ObjService, |
| userCommon *usercommon.UserCommon, |
| userRepo usercommon.UserRepo, |
| questionRepo questioncommon.QuestionRepo, |
| answerRepo answercommon.AnswerRepo, |
| userRoleService *role.UserRoleRelService, |
| externalNotificationQueueService notice_queue.ExternalNotificationQueueService, |
| tagCommon *tagcommon.TagCommonService, |
| questionCommon *questioncommon.QuestionCommon, |
| notificationQueueService notice_queue.NotificationQueueService, |
| siteInfoService siteinfo_common.SiteInfoCommonService, |
| commentCommonRepo commentcommon.CommentCommonRepo, |
| ) *ReviewService { |
| return &ReviewService{ |
| reviewRepo: reviewRepo, |
| objectInfoService: objectInfoService, |
| userCommon: userCommon, |
| userRepo: userRepo, |
| questionRepo: questionRepo, |
| answerRepo: answerRepo, |
| userRoleService: userRoleService, |
| externalNotificationQueueService: externalNotificationQueueService, |
| tagCommon: tagCommon, |
| questionCommon: questionCommon, |
| notificationQueueService: notificationQueueService, |
| siteInfoService: siteInfoService, |
| commentCommonRepo: commentCommonRepo, |
| } |
| } |
| |
| // AddQuestionReview add review for question if needed |
| func (cs *ReviewService) AddQuestionReview(ctx context.Context, |
| question *entity.Question, tags []*schema.TagItem, ip, ua string) (questionStatus int) { |
| reviewContent := &plugin.ReviewContent{ |
| ObjectType: constant.QuestionObjectType, |
| Title: question.Title, |
| Content: question.ParsedText, |
| IP: ip, |
| UserAgent: ua, |
| } |
| for _, tag := range tags { |
| reviewContent.Tags = append(reviewContent.Tags, tag.SlugName) |
| } |
| reviewContent.Author = cs.getReviewContentAuthorInfo(ctx, question.UserID) |
| reviewStatus := cs.callPluginToReview(ctx, question.UserID, question.ID, reviewContent) |
| switch reviewStatus { |
| case plugin.ReviewStatusApproved: |
| questionStatus = entity.QuestionStatusAvailable |
| case plugin.ReviewStatusNeedReview: |
| questionStatus = entity.QuestionStatusPending |
| case plugin.ReviewStatusDeleteDirectly: |
| questionStatus = entity.QuestionStatusDeleted |
| default: |
| questionStatus = entity.QuestionStatusAvailable |
| } |
| return questionStatus |
| } |
| |
| // AddAnswerReview add review for answer if needed |
| func (cs *ReviewService) AddAnswerReview(ctx context.Context, |
| answer *entity.Answer, ip, ua string) (answerStatus int) { |
| reviewContent := &plugin.ReviewContent{ |
| ObjectType: constant.AnswerObjectType, |
| Content: answer.ParsedText, |
| IP: ip, |
| UserAgent: ua, |
| } |
| reviewContent.Author = cs.getReviewContentAuthorInfo(ctx, answer.UserID) |
| reviewStatus := cs.callPluginToReview(ctx, answer.UserID, answer.ID, reviewContent) |
| switch reviewStatus { |
| case plugin.ReviewStatusApproved: |
| answerStatus = entity.AnswerStatusAvailable |
| case plugin.ReviewStatusNeedReview: |
| answerStatus = entity.AnswerStatusPending |
| case plugin.ReviewStatusDeleteDirectly: |
| answerStatus = entity.AnswerStatusDeleted |
| default: |
| answerStatus = entity.AnswerStatusAvailable |
| } |
| return answerStatus |
| } |
| |
| // AddCommentReview add review for comment if needed |
| func (cs *ReviewService) AddCommentReview(ctx context.Context, |
| comment *entity.Comment, ip, ua string) (commentStatus int) { |
| reviewContent := &plugin.ReviewContent{ |
| ObjectType: constant.CommentObjectType, |
| Content: comment.ParsedText, |
| IP: ip, |
| UserAgent: ua, |
| } |
| reviewContent.Author = cs.getReviewContentAuthorInfo(ctx, comment.UserID) |
| reviewStatus := cs.callPluginToReview(ctx, comment.UserID, comment.ID, reviewContent) |
| switch reviewStatus { |
| case plugin.ReviewStatusApproved: |
| commentStatus = entity.CommentStatusAvailable |
| case plugin.ReviewStatusNeedReview: |
| commentStatus = entity.CommentStatusPending |
| case plugin.ReviewStatusDeleteDirectly: |
| commentStatus = entity.CommentStatusDeleted |
| default: |
| commentStatus = entity.CommentStatusAvailable |
| } |
| return commentStatus |
| } |
| |
| // get review content author info |
| func (cs *ReviewService) getReviewContentAuthorInfo(ctx context.Context, userID string) (author plugin.ReviewContentAuthor) { |
| user, exist, err := cs.userCommon.GetUserBasicInfoByID(ctx, userID) |
| if err != nil { |
| log.Errorf("get user info failed, err: %v", err) |
| return |
| } |
| if !exist { |
| log.Errorf("user not found by id: %s", userID) |
| return |
| } |
| author.Rank = user.Rank |
| author.ApprovedQuestionAmount, _ = cs.questionRepo.GetUserQuestionCount(ctx, userID, 0) |
| author.ApprovedAnswerAmount, _ = cs.answerRepo.GetCountByUserID(ctx, userID) |
| author.Role, _ = cs.userRoleService.GetUserRole(ctx, userID) |
| return |
| } |
| |
| // call plugin to review |
| func (cs *ReviewService) callPluginToReview(ctx context.Context, userID, objectID string, |
| reviewContent *plugin.ReviewContent) (reviewStatus plugin.ReviewStatus) { |
| // As default, no need review |
| reviewStatus = plugin.ReviewStatusApproved |
| objectID = uid.DeShortID(objectID) |
| |
| r := &entity.Review{ |
| UserID: userID, |
| ObjectID: objectID, |
| ObjectType: constant.ObjectTypeStrMapping[reviewContent.ObjectType], |
| ReviewerUserID: "0", |
| Status: entity.ReviewStatusPending, |
| } |
| if siteInterface, _ := cs.siteInfoService.GetSiteInterface(ctx); siteInterface != nil { |
| reviewContent.Language = siteInterface.Language |
| } |
| |
| _ = plugin.CallReviewer(func(reviewer plugin.Reviewer) error { |
| // If one of the reviewer plugin return false, then the review is not approved |
| if reviewStatus != plugin.ReviewStatusApproved { |
| return nil |
| } |
| if result := reviewer.Review(reviewContent); !result.Approved { |
| reviewStatus = result.ReviewStatus |
| r.Reason = result.Reason |
| r.Submitter = reviewer.Info().SlugName |
| } |
| return nil |
| }) |
| |
| if reviewStatus == plugin.ReviewStatusNeedReview { |
| if err := cs.reviewRepo.AddReview(ctx, r); err != nil { |
| log.Errorf("add review failed, err: %v", err) |
| } |
| } |
| return reviewStatus |
| } |
| |
| // UpdateReview update review |
| func (cs *ReviewService) UpdateReview(ctx context.Context, req *schema.UpdateReviewReq) (err error) { |
| review, exist, err := cs.reviewRepo.GetReview(ctx, req.ReviewID) |
| if err != nil { |
| return err |
| } |
| if !exist { |
| return errors.BadRequest(reason.ObjectNotFound) |
| } |
| if review.Status != entity.ReviewStatusPending { |
| return nil |
| } |
| |
| if err = cs.updateObjectStatus(ctx, review, req.IsApprove()); err != nil { |
| return err |
| } |
| |
| if req.IsApprove() { |
| err = cs.reviewRepo.UpdateReviewStatus(ctx, req.ReviewID, req.UserID, entity.ReviewStatusApproved) |
| } else { |
| err = cs.reviewRepo.UpdateReviewStatus(ctx, req.ReviewID, req.UserID, entity.ReviewStatusRejected) |
| } |
| return |
| } |
| |
| // update object status |
| func (cs *ReviewService) updateObjectStatus(ctx context.Context, review *entity.Review, isApprove bool) (err error) { |
| objectType := constant.ObjectTypeNumberMapping[review.ObjectType] |
| switch objectType { |
| case constant.QuestionObjectType: |
| questionInfo, exist, err := cs.questionRepo.GetQuestion(ctx, review.ObjectID) |
| if err != nil { |
| return err |
| } |
| if !exist { |
| return errors.BadRequest(reason.ObjectNotFound) |
| } |
| if isApprove { |
| questionInfo.Status = entity.QuestionStatusAvailable |
| } else { |
| questionInfo.Status = entity.QuestionStatusDeleted |
| } |
| if err := cs.questionRepo.UpdateQuestionStatus(ctx, questionInfo.ID, questionInfo.Status); err != nil { |
| return err |
| } |
| if isApprove { |
| tags, err := cs.tagCommon.GetObjectEntityTag(ctx, questionInfo.ID) |
| if err != nil { |
| log.Errorf("get question tags failed, err: %v", err) |
| } |
| cs.externalNotificationQueueService.Send(ctx, |
| schema.CreateNewQuestionNotificationMsg(questionInfo.ID, questionInfo.Title, questionInfo.UserID, tags)) |
| } |
| userQuestionCount, err := cs.questionRepo.GetUserQuestionCount(ctx, questionInfo.UserID, 0) |
| if err != nil { |
| log.Errorf("get user question count failed, err: %v", err) |
| } else { |
| err = cs.userCommon.UpdateQuestionCount(ctx, questionInfo.UserID, userQuestionCount) |
| if err != nil { |
| log.Errorf("update user question count failed, err: %v", err) |
| } |
| } |
| case constant.AnswerObjectType: |
| answerInfo, exist, err := cs.answerRepo.GetAnswer(ctx, review.ObjectID) |
| if err != nil { |
| return err |
| } |
| if !exist { |
| return errors.BadRequest(reason.ObjectNotFound) |
| } |
| if isApprove { |
| answerInfo.Status = entity.AnswerStatusAvailable |
| } else { |
| answerInfo.Status = entity.AnswerStatusDeleted |
| } |
| if err := cs.answerRepo.UpdateAnswerStatus(ctx, answerInfo.ID, answerInfo.Status); err != nil { |
| return err |
| } |
| questionInfo, exist, err := cs.questionRepo.GetQuestion(ctx, answerInfo.QuestionID) |
| if err != nil { |
| return err |
| } |
| if !exist { |
| return errors.BadRequest(reason.ObjectNotFound) |
| } |
| if isApprove { |
| cs.notificationAnswerTheQuestion(ctx, questionInfo.UserID, questionInfo.ID, answerInfo.ID, |
| answerInfo.UserID, questionInfo.Title, answerInfo.OriginalText) |
| } |
| if err := cs.questionCommon.UpdateAnswerCount(ctx, answerInfo.QuestionID); err != nil { |
| log.Errorf("update question answer count failed, err: %v", err) |
| } |
| if err := cs.questionCommon.UpdateLastAnswer(ctx, answerInfo.QuestionID, uid.DeShortID(answerInfo.ID)); err != nil { |
| log.Errorf("update question last answer failed, err: %v", err) |
| } |
| userAnswerCount, err := cs.answerRepo.GetCountByUserID(ctx, answerInfo.UserID) |
| if err != nil { |
| log.Errorf("get user answer count failed, err: %v", err) |
| } else { |
| err = cs.userCommon.UpdateAnswerCount(ctx, answerInfo.UserID, int(userAnswerCount)) |
| if err != nil { |
| log.Errorf("update user answer count failed, err: %v", err) |
| } |
| } |
| case constant.CommentObjectType: |
| commentInfo, exist, err := cs.commentCommonRepo.GetCommentWithoutStatus(ctx, review.ObjectID) |
| if err != nil { |
| return err |
| } |
| if !exist { |
| return errors.BadRequest(reason.ObjectNotFound) |
| } |
| if isApprove { |
| commentInfo.Status = entity.CommentStatusAvailable |
| } else { |
| commentInfo.Status = entity.CommentStatusDeleted |
| } |
| if err := cs.commentCommonRepo.UpdateCommentStatus(ctx, commentInfo.ID, commentInfo.Status); err != nil { |
| return err |
| } |
| _, exist, err = cs.questionRepo.GetQuestion(ctx, commentInfo.QuestionID) |
| if err != nil { |
| return err |
| } |
| if !exist { |
| return errors.BadRequest(reason.ObjectNotFound) |
| } |
| if isApprove { |
| cs.notificationCommentOnTheQuestion(ctx, commentInfo) |
| } |
| } |
| return |
| } |
| |
| func (cs *ReviewService) notificationAnswerTheQuestion(ctx context.Context, |
| questionUserID, questionID, answerID, answerUserID, questionTitle, answerSummary string) { |
| // If the question is answered by me, there is no notification for myself. |
| if questionUserID == answerUserID { |
| return |
| } |
| msg := &schema.NotificationMsg{ |
| TriggerUserID: answerUserID, |
| ReceiverUserID: questionUserID, |
| Type: schema.NotificationTypeInbox, |
| ObjectID: answerID, |
| } |
| msg.ObjectType = constant.AnswerObjectType |
| msg.NotificationAction = constant.NotificationAnswerTheQuestion |
| cs.notificationQueueService.Send(ctx, msg) |
| |
| receiverUserInfo, exist, err := cs.userRepo.GetByUserID(ctx, questionUserID) |
| if err != nil { |
| log.Error(err) |
| return |
| } |
| if !exist { |
| log.Warnf("user %s not found", questionUserID) |
| return |
| } |
| |
| externalNotificationMsg := &schema.ExternalNotificationMsg{ |
| ReceiverUserID: receiverUserInfo.ID, |
| ReceiverEmail: receiverUserInfo.EMail, |
| ReceiverLang: receiverUserInfo.Language, |
| } |
| rawData := &schema.NewAnswerTemplateRawData{ |
| QuestionTitle: questionTitle, |
| QuestionID: questionID, |
| AnswerID: answerID, |
| AnswerSummary: answerSummary, |
| UnsubscribeCode: token.GenerateToken(), |
| } |
| answerUser, _, _ := cs.userCommon.GetUserBasicInfoByID(ctx, answerUserID) |
| if answerUser != nil { |
| rawData.AnswerUserDisplayName = answerUser.DisplayName |
| } |
| externalNotificationMsg.NewAnswerTemplateRawData = rawData |
| cs.externalNotificationQueueService.Send(ctx, externalNotificationMsg) |
| } |
| |
| func (cs *ReviewService) notificationCommentOnTheQuestion(ctx context.Context, comment *entity.Comment) { |
| objInfo, err := cs.objectInfoService.GetInfo(ctx, comment.ObjectID) |
| if err != nil { |
| log.Error(err) |
| return |
| } |
| if objInfo.IsDeleted() { |
| log.Error("object already deleted") |
| return |
| } |
| objInfo.ObjectID = uid.DeShortID(objInfo.ObjectID) |
| objInfo.QuestionID = uid.DeShortID(objInfo.QuestionID) |
| objInfo.AnswerID = uid.DeShortID(objInfo.AnswerID) |
| |
| // The priority of the notification |
| // 1. reply to user |
| // 2. comment mention to user |
| // 3. answer or question was commented |
| alreadyNotifiedUserID := make(map[string]bool) |
| |
| // get reply user info |
| replyUserID := comment.GetReplyUserID() |
| if len(replyUserID) > 0 && replyUserID != comment.UserID { |
| replyUser, _, err := cs.userCommon.GetUserBasicInfoByID(ctx, replyUserID) |
| if err != nil { |
| log.Error(err) |
| return |
| } |
| cs.notificationCommentReply(ctx, replyUser.ID, comment.ID, comment.UserID, |
| objInfo.QuestionID, objInfo.Title, htmltext.FetchExcerpt(comment.ParsedText, "...", 240)) |
| alreadyNotifiedUserID[replyUser.ID] = true |
| return |
| } |
| |
| mentionUsernameList := comment.GetMentionUsernameList() |
| if len(mentionUsernameList) > 0 { |
| alreadyNotifiedUserIDs := cs.notificationMention( |
| ctx, mentionUsernameList, comment.ID, comment.UserID, alreadyNotifiedUserID) |
| for _, userID := range alreadyNotifiedUserIDs { |
| alreadyNotifiedUserID[userID] = true |
| } |
| return |
| } |
| |
| if objInfo.ObjectType == constant.QuestionObjectType && !alreadyNotifiedUserID[objInfo.ObjectCreatorUserID] { |
| cs.notificationQuestionComment(ctx, objInfo.ObjectCreatorUserID, |
| objInfo.QuestionID, objInfo.Title, comment.ID, comment.UserID, htmltext.FetchExcerpt(comment.ParsedText, "...", 240)) |
| } else if objInfo.ObjectType == constant.AnswerObjectType && !alreadyNotifiedUserID[objInfo.ObjectCreatorUserID] { |
| cs.notificationAnswerComment(ctx, objInfo.QuestionID, objInfo.Title, objInfo.AnswerID, |
| objInfo.ObjectCreatorUserID, comment.ID, comment.UserID, htmltext.FetchExcerpt(comment.ParsedText, "...", 240)) |
| } |
| return |
| } |
| |
| func (cs *ReviewService) notificationCommentReply(ctx context.Context, replyUserID, commentID, commentUserID, |
| questionID, questionTitle, commentSummary string) { |
| msg := &schema.NotificationMsg{ |
| ReceiverUserID: replyUserID, |
| TriggerUserID: commentUserID, |
| Type: schema.NotificationTypeInbox, |
| ObjectID: commentID, |
| } |
| msg.ObjectType = constant.CommentObjectType |
| msg.NotificationAction = constant.NotificationReplyToYou |
| cs.notificationQueueService.Send(ctx, msg) |
| |
| // Send external notification. |
| receiverUserInfo, exist, err := cs.userRepo.GetByUserID(ctx, replyUserID) |
| if err != nil { |
| log.Error(err) |
| return |
| } |
| if !exist { |
| log.Warnf("user %s not found", replyUserID) |
| return |
| } |
| externalNotificationMsg := &schema.ExternalNotificationMsg{ |
| ReceiverUserID: receiverUserInfo.ID, |
| ReceiverEmail: receiverUserInfo.EMail, |
| ReceiverLang: receiverUserInfo.Language, |
| } |
| rawData := &schema.NewCommentTemplateRawData{ |
| QuestionTitle: questionTitle, |
| QuestionID: questionID, |
| CommentID: commentID, |
| CommentSummary: commentSummary, |
| UnsubscribeCode: token.GenerateToken(), |
| } |
| commentUser, _, _ := cs.userCommon.GetUserBasicInfoByID(ctx, commentUserID) |
| if commentUser != nil { |
| rawData.CommentUserDisplayName = commentUser.DisplayName |
| } |
| externalNotificationMsg.NewCommentTemplateRawData = rawData |
| cs.externalNotificationQueueService.Send(ctx, externalNotificationMsg) |
| } |
| |
| func (cs *ReviewService) notificationMention( |
| ctx context.Context, mentionUsernameList []string, commentID, commentUserID string, |
| alreadyNotifiedUserID map[string]bool) (alreadyNotifiedUserIDs []string) { |
| for _, username := range mentionUsernameList { |
| userInfo, exist, err := cs.userCommon.GetUserBasicInfoByUserName(ctx, username) |
| if err != nil { |
| log.Error(err) |
| continue |
| } |
| if exist && !alreadyNotifiedUserID[userInfo.ID] { |
| msg := &schema.NotificationMsg{ |
| ReceiverUserID: userInfo.ID, |
| TriggerUserID: commentUserID, |
| Type: schema.NotificationTypeInbox, |
| ObjectID: commentID, |
| } |
| msg.ObjectType = constant.CommentObjectType |
| msg.NotificationAction = constant.NotificationMentionYou |
| cs.notificationQueueService.Send(ctx, msg) |
| alreadyNotifiedUserIDs = append(alreadyNotifiedUserIDs, userInfo.ID) |
| } |
| } |
| return alreadyNotifiedUserIDs |
| } |
| |
| func (cs *ReviewService) notificationQuestionComment(ctx context.Context, questionUserID, |
| questionID, questionTitle, commentID, commentUserID, commentSummary string) { |
| if questionUserID == commentUserID { |
| return |
| } |
| // send internal notification |
| msg := &schema.NotificationMsg{ |
| ReceiverUserID: questionUserID, |
| TriggerUserID: commentUserID, |
| Type: schema.NotificationTypeInbox, |
| ObjectID: commentID, |
| } |
| msg.ObjectType = constant.CommentObjectType |
| msg.NotificationAction = constant.NotificationCommentQuestion |
| cs.notificationQueueService.Send(ctx, msg) |
| |
| // send external notification |
| receiverUserInfo, exist, err := cs.userRepo.GetByUserID(ctx, questionUserID) |
| if err != nil { |
| log.Error(err) |
| return |
| } |
| if !exist { |
| log.Warnf("user %s not found", questionUserID) |
| return |
| } |
| |
| externalNotificationMsg := &schema.ExternalNotificationMsg{ |
| ReceiverUserID: receiverUserInfo.ID, |
| ReceiverEmail: receiverUserInfo.EMail, |
| ReceiverLang: receiverUserInfo.Language, |
| } |
| rawData := &schema.NewCommentTemplateRawData{ |
| QuestionTitle: questionTitle, |
| QuestionID: questionID, |
| CommentID: commentID, |
| CommentSummary: commentSummary, |
| UnsubscribeCode: token.GenerateToken(), |
| } |
| commentUser, _, _ := cs.userCommon.GetUserBasicInfoByID(ctx, commentUserID) |
| if commentUser != nil { |
| rawData.CommentUserDisplayName = commentUser.DisplayName |
| } |
| externalNotificationMsg.NewCommentTemplateRawData = rawData |
| cs.externalNotificationQueueService.Send(ctx, externalNotificationMsg) |
| } |
| |
| func (cs *ReviewService) notificationAnswerComment(ctx context.Context, |
| questionID, questionTitle, answerID, answerUserID, commentID, commentUserID, commentSummary string) { |
| if answerUserID == commentUserID { |
| return |
| } |
| |
| // Send internal notification. |
| msg := &schema.NotificationMsg{ |
| ReceiverUserID: answerUserID, |
| TriggerUserID: commentUserID, |
| Type: schema.NotificationTypeInbox, |
| ObjectID: commentID, |
| } |
| msg.ObjectType = constant.CommentObjectType |
| msg.NotificationAction = constant.NotificationCommentAnswer |
| cs.notificationQueueService.Send(ctx, msg) |
| |
| // Send external notification. |
| receiverUserInfo, exist, err := cs.userRepo.GetByUserID(ctx, answerUserID) |
| if err != nil { |
| log.Error(err) |
| return |
| } |
| if !exist { |
| log.Warnf("user %s not found", answerUserID) |
| return |
| } |
| externalNotificationMsg := &schema.ExternalNotificationMsg{ |
| ReceiverUserID: receiverUserInfo.ID, |
| ReceiverEmail: receiverUserInfo.EMail, |
| ReceiverLang: receiverUserInfo.Language, |
| } |
| rawData := &schema.NewCommentTemplateRawData{ |
| QuestionTitle: questionTitle, |
| QuestionID: questionID, |
| AnswerID: answerID, |
| CommentID: commentID, |
| CommentSummary: commentSummary, |
| UnsubscribeCode: token.GenerateToken(), |
| } |
| commentUser, _, _ := cs.userCommon.GetUserBasicInfoByID(ctx, commentUserID) |
| if commentUser != nil { |
| rawData.CommentUserDisplayName = commentUser.DisplayName |
| } |
| externalNotificationMsg.NewCommentTemplateRawData = rawData |
| cs.externalNotificationQueueService.Send(ctx, externalNotificationMsg) |
| } |
| |
| // GetReviewPendingCount get review pending count |
| func (cs *ReviewService) GetReviewPendingCount(ctx context.Context) (count int64, err error) { |
| return cs.reviewRepo.GetReviewCount(ctx, entity.ReviewStatusPending) |
| } |
| |
| // GetUnreviewedPostPage get review page |
| func (cs *ReviewService) GetUnreviewedPostPage(ctx context.Context, req *schema.GetUnreviewedPostPageReq) ( |
| pageModel *pager.PageModel, err error) { |
| if !req.IsAdmin { |
| return pager.NewPageModel(0, make([]*schema.GetUnreviewedPostPageResp, 0)), nil |
| } |
| cond := &entity.Review{ |
| ObjectID: req.ObjectID, |
| Status: entity.ReviewStatusPending, |
| } |
| reviewList, total, err := cs.reviewRepo.GetReviewPage(ctx, req.Page, 1, cond) |
| if err != nil { |
| return |
| } |
| |
| resp := make([]*schema.GetUnreviewedPostPageResp, 0) |
| for _, review := range reviewList { |
| info, err := cs.objectInfoService.GetUnreviewedRevisionInfo(ctx, review.ObjectID) |
| if err != nil { |
| log.Errorf("GetUnreviewedRevisionInfo failed, err: %v", err) |
| continue |
| } |
| |
| r := &schema.GetUnreviewedPostPageResp{ |
| ReviewID: review.ID, |
| CreatedAt: info.CreatedAt, |
| ObjectID: info.ObjectID, |
| QuestionID: info.QuestionID, |
| AnswerID: info.AnswerID, |
| CommentID: info.CommentID, |
| ObjectType: info.ObjectType, |
| Title: info.Title, |
| UrlTitle: htmltext.UrlTitle(info.Title), |
| OriginalText: info.Content, |
| ParsedText: info.Html, |
| Tags: info.Tags, |
| ObjectStatus: info.Status, |
| ObjectShowStatus: info.ShowStatus, |
| SubmitAt: review.CreatedAt.Unix(), |
| SubmitterDisplayName: req.ReviewerMapping[review.Submitter], |
| Reason: review.Reason, |
| } |
| |
| // get user info |
| userInfo, exists, e := cs.userCommon.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) |
| } |
| resp = append(resp, r) |
| } |
| return pager.NewPageModel(total, resp), nil |
| } |