2022-09-27 17:59:05 +08:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2023-08-22 14:48:40 +08:00
|
|
|
"github.com/answerdev/answer/pkg/token"
|
2022-09-27 17:59:05 +08:00
|
|
|
"time"
|
|
|
|
|
2022-10-24 16:51:05 +08:00
|
|
|
"github.com/answerdev/answer/internal/base/constant"
|
|
|
|
"github.com/answerdev/answer/internal/base/reason"
|
|
|
|
"github.com/answerdev/answer/internal/entity"
|
|
|
|
"github.com/answerdev/answer/internal/schema"
|
2022-11-23 17:02:44 +08:00
|
|
|
"github.com/answerdev/answer/internal/service/activity"
|
|
|
|
"github.com/answerdev/answer/internal/service/activity_common"
|
|
|
|
"github.com/answerdev/answer/internal/service/activity_queue"
|
2022-10-24 16:51:05 +08:00
|
|
|
answercommon "github.com/answerdev/answer/internal/service/answer_common"
|
|
|
|
collectioncommon "github.com/answerdev/answer/internal/service/collection_common"
|
2022-12-27 17:42:23 +08:00
|
|
|
"github.com/answerdev/answer/internal/service/export"
|
2022-11-23 17:02:44 +08:00
|
|
|
"github.com/answerdev/answer/internal/service/notice_queue"
|
2022-10-24 16:51:05 +08:00
|
|
|
"github.com/answerdev/answer/internal/service/permission"
|
|
|
|
questioncommon "github.com/answerdev/answer/internal/service/question_common"
|
2022-11-23 17:02:44 +08:00
|
|
|
"github.com/answerdev/answer/internal/service/revision_common"
|
2023-03-03 16:59:34 +08:00
|
|
|
"github.com/answerdev/answer/internal/service/role"
|
2022-10-24 16:51:05 +08:00
|
|
|
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
2023-03-17 10:54:54 +08:00
|
|
|
"github.com/answerdev/answer/pkg/uid"
|
2022-09-27 17:59:05 +08:00
|
|
|
"github.com/segmentfault/pacman/errors"
|
|
|
|
"github.com/segmentfault/pacman/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
// AnswerService user service
|
|
|
|
type AnswerService struct {
|
2023-08-22 11:59:33 +08:00
|
|
|
answerRepo answercommon.AnswerRepo
|
|
|
|
questionRepo questioncommon.QuestionRepo
|
|
|
|
questionCommon *questioncommon.QuestionCommon
|
|
|
|
answerActivityService *activity.AnswerActivityService
|
|
|
|
userCommon *usercommon.UserCommon
|
|
|
|
collectionCommon *collectioncommon.CollectionCommon
|
|
|
|
userRepo usercommon.UserRepo
|
|
|
|
revisionService *revision_common.RevisionService
|
|
|
|
AnswerCommon *answercommon.AnswerCommon
|
|
|
|
voteRepo activity_common.VoteRepo
|
|
|
|
emailService *export.EmailService
|
|
|
|
roleService *role.UserRoleRelService
|
|
|
|
notificationQueueService notice_queue.NotificationQueueService
|
|
|
|
externalNotificationQueueService notice_queue.ExternalNotificationQueueService
|
|
|
|
activityQueueService activity_queue.ActivityQueueService
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewAnswerService(
|
|
|
|
answerRepo answercommon.AnswerRepo,
|
|
|
|
questionRepo questioncommon.QuestionRepo,
|
|
|
|
questionCommon *questioncommon.QuestionCommon,
|
|
|
|
userCommon *usercommon.UserCommon,
|
|
|
|
collectionCommon *collectioncommon.CollectionCommon,
|
|
|
|
userRepo usercommon.UserRepo,
|
|
|
|
revisionService *revision_common.RevisionService,
|
|
|
|
answerAcceptActivityRepo *activity.AnswerActivityService,
|
|
|
|
answerCommon *answercommon.AnswerCommon,
|
|
|
|
voteRepo activity_common.VoteRepo,
|
2022-12-27 17:42:23 +08:00
|
|
|
emailService *export.EmailService,
|
2023-03-03 16:59:34 +08:00
|
|
|
roleService *role.UserRoleRelService,
|
2023-06-02 18:15:54 +08:00
|
|
|
notificationQueueService notice_queue.NotificationQueueService,
|
2023-08-22 11:59:33 +08:00
|
|
|
externalNotificationQueueService notice_queue.ExternalNotificationQueueService,
|
2023-06-05 14:10:04 +08:00
|
|
|
activityQueueService activity_queue.ActivityQueueService,
|
2022-09-27 17:59:05 +08:00
|
|
|
) *AnswerService {
|
|
|
|
return &AnswerService{
|
2023-08-22 11:59:33 +08:00
|
|
|
answerRepo: answerRepo,
|
|
|
|
questionRepo: questionRepo,
|
|
|
|
userCommon: userCommon,
|
|
|
|
collectionCommon: collectionCommon,
|
|
|
|
questionCommon: questionCommon,
|
|
|
|
userRepo: userRepo,
|
|
|
|
revisionService: revisionService,
|
|
|
|
answerActivityService: answerAcceptActivityRepo,
|
|
|
|
AnswerCommon: answerCommon,
|
|
|
|
voteRepo: voteRepo,
|
|
|
|
emailService: emailService,
|
|
|
|
roleService: roleService,
|
|
|
|
notificationQueueService: notificationQueueService,
|
|
|
|
externalNotificationQueueService: externalNotificationQueueService,
|
|
|
|
activityQueueService: activityQueueService,
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveAnswer delete answer
|
2022-11-04 16:59:16 +08:00
|
|
|
func (as *AnswerService) RemoveAnswer(ctx context.Context, req *schema.RemoveAnswerReq) (err error) {
|
|
|
|
answerInfo, exist, err := as.answerRepo.GetByID(ctx, req.ID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !exist {
|
|
|
|
return nil
|
|
|
|
}
|
2023-02-15 15:01:58 +08:00
|
|
|
// if the status is deleted, return directly
|
|
|
|
if answerInfo.Status == entity.AnswerStatusDeleted {
|
|
|
|
return nil
|
|
|
|
}
|
2023-03-03 16:59:34 +08:00
|
|
|
roleID, err := as.roleService.GetUserRole(ctx, req.UserID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if roleID != role.RoleAdminID && roleID != role.RoleModeratorID {
|
2022-11-22 16:54:28 +08:00
|
|
|
if answerInfo.UserID != req.UserID {
|
|
|
|
return errors.BadRequest(reason.AnswerCannotDeleted)
|
|
|
|
}
|
|
|
|
if answerInfo.VoteCount > 0 {
|
|
|
|
return errors.BadRequest(reason.AnswerCannotDeleted)
|
|
|
|
}
|
2022-12-22 11:04:19 +08:00
|
|
|
if answerInfo.Accepted == schema.AnswerAcceptedEnable {
|
2022-11-22 16:54:28 +08:00
|
|
|
return errors.BadRequest(reason.AnswerCannotDeleted)
|
|
|
|
}
|
2023-03-07 17:35:20 +08:00
|
|
|
_, exist, err := as.questionRepo.GetQuestion(ctx, answerInfo.QuestionID)
|
2022-11-22 16:54:28 +08:00
|
|
|
if err != nil {
|
|
|
|
return errors.BadRequest(reason.AnswerCannotDeleted)
|
|
|
|
}
|
|
|
|
if !exist {
|
|
|
|
return errors.BadRequest(reason.AnswerCannotDeleted)
|
|
|
|
}
|
2023-03-07 17:35:20 +08:00
|
|
|
|
2022-11-11 15:35:08 +08:00
|
|
|
}
|
2022-09-27 17:59:05 +08:00
|
|
|
|
2023-06-06 15:43:32 +08:00
|
|
|
err = as.answerRepo.RemoveAnswer(ctx, req.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-11-01 15:25:44 +08:00
|
|
|
// user add question count
|
2023-06-06 15:43:32 +08:00
|
|
|
err = as.questionCommon.UpdateAnswerCount(ctx, answerInfo.QuestionID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error("IncreaseAnswerCount error", err.Error())
|
|
|
|
}
|
2023-06-06 15:43:32 +08:00
|
|
|
userAnswerCount, err := as.answerRepo.GetCountByUserID(ctx, answerInfo.UserID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
2023-06-06 15:43:32 +08:00
|
|
|
log.Error("GetCountByUserID error", err.Error())
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
2023-06-06 15:43:32 +08:00
|
|
|
err = as.userCommon.UpdateAnswerCount(ctx, answerInfo.UserID, int(userAnswerCount))
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
2023-06-06 15:43:32 +08:00
|
|
|
log.Error("user IncreaseAnswerCount error", err.Error())
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
2023-05-22 15:26:20 +08:00
|
|
|
// #2372 In order to simplify the process and complexity, as well as to consider if it is in-house,
|
|
|
|
// facing the problem of recovery.
|
|
|
|
//err = as.answerActivityService.DeleteAnswer(ctx, answerInfo.ID, answerInfo.CreatedAt, answerInfo.VoteCount)
|
|
|
|
//if err != nil {
|
|
|
|
// log.Errorf("delete answer activity change failed: %s", err.Error())
|
|
|
|
//}
|
2023-06-05 14:10:04 +08:00
|
|
|
as.activityQueueService.Send(ctx, &schema.ActivityMsg{
|
2022-11-23 17:02:44 +08:00
|
|
|
UserID: req.UserID,
|
|
|
|
ObjectID: answerInfo.ID,
|
|
|
|
OriginalObjectID: answerInfo.ID,
|
|
|
|
ActivityTypeKey: constant.ActAnswerDeleted,
|
|
|
|
})
|
2022-09-27 17:59:05 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (string, error) {
|
2022-11-01 15:25:44 +08:00
|
|
|
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if !exist {
|
|
|
|
return "", errors.BadRequest(reason.QuestionNotFound)
|
|
|
|
}
|
2023-03-15 17:34:16 +08:00
|
|
|
if questionInfo.Status == entity.QuestionStatusClosed || questionInfo.Status == entity.QuestionStatusDeleted {
|
|
|
|
err = errors.BadRequest(reason.AnswerCannotAddByClosedQuestion)
|
|
|
|
return "", err
|
|
|
|
}
|
2022-09-27 17:59:05 +08:00
|
|
|
insertData := new(entity.Answer)
|
|
|
|
insertData.UserID = req.UserID
|
|
|
|
insertData.OriginalText = req.Content
|
2022-11-01 15:25:44 +08:00
|
|
|
insertData.ParsedText = req.HTML
|
2022-12-22 11:04:19 +08:00
|
|
|
insertData.Accepted = schema.AnswerAcceptedFailed
|
2022-11-01 15:25:44 +08:00
|
|
|
insertData.QuestionID = req.QuestionID
|
2022-09-27 17:59:05 +08:00
|
|
|
insertData.RevisionID = "0"
|
2022-12-02 13:13:04 +08:00
|
|
|
insertData.LastEditUserID = "0"
|
2022-09-27 17:59:05 +08:00
|
|
|
insertData.Status = entity.AnswerStatusAvailable
|
2022-12-01 14:37:17 +08:00
|
|
|
//insertData.UpdatedAt = now
|
2022-11-01 15:25:44 +08:00
|
|
|
if err = as.answerRepo.AddAnswer(ctx, insertData); err != nil {
|
2022-09-27 17:59:05 +08:00
|
|
|
return "", err
|
|
|
|
}
|
2023-06-06 15:43:32 +08:00
|
|
|
err = as.questionCommon.UpdateAnswerCount(ctx, req.QuestionID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error("IncreaseAnswerCount error", err.Error())
|
|
|
|
}
|
2023-03-17 10:54:54 +08:00
|
|
|
err = as.questionCommon.UpdateLastAnswer(ctx, req.QuestionID, uid.DeShortID(insertData.ID))
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error("UpdateLastAnswer error", err.Error())
|
|
|
|
}
|
2023-02-24 17:10:43 +08:00
|
|
|
err = as.questionCommon.UpdatePostTime(ctx, req.QuestionID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return insertData.ID, err
|
|
|
|
}
|
2023-06-06 15:43:32 +08:00
|
|
|
userAnswerCount, err := as.answerRepo.GetCountByUserID(ctx, req.UserID)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("GetCountByUserID error", err.Error())
|
|
|
|
}
|
|
|
|
err = as.userCommon.UpdateAnswerCount(ctx, req.UserID, int(userAnswerCount))
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error("user IncreaseAnswerCount error", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
revisionDTO := &schema.AddRevisionDTO{
|
|
|
|
UserID: insertData.UserID,
|
|
|
|
ObjectID: insertData.ID,
|
|
|
|
Title: "",
|
|
|
|
}
|
2022-11-01 15:25:44 +08:00
|
|
|
infoJSON, _ := json.Marshal(insertData)
|
|
|
|
revisionDTO.Content = string(infoJSON)
|
2022-11-22 19:48:27 +08:00
|
|
|
revisionID, err := as.revisionService.AddRevision(ctx, revisionDTO, true)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return insertData.ID, err
|
|
|
|
}
|
2022-12-27 17:42:23 +08:00
|
|
|
as.notificationAnswerTheQuestion(ctx, questionInfo.UserID, questionInfo.ID, insertData.ID, req.UserID, questionInfo.Title,
|
|
|
|
insertData.OriginalText)
|
2022-11-21 19:28:02 +08:00
|
|
|
|
2023-06-05 14:10:04 +08:00
|
|
|
as.activityQueueService.Send(ctx, &schema.ActivityMsg{
|
2022-11-22 19:48:27 +08:00
|
|
|
UserID: insertData.UserID,
|
|
|
|
ObjectID: insertData.ID,
|
|
|
|
OriginalObjectID: insertData.ID,
|
|
|
|
ActivityTypeKey: constant.ActAnswerAnswered,
|
|
|
|
RevisionID: revisionID,
|
2022-11-21 19:28:02 +08:00
|
|
|
})
|
2023-06-05 14:10:04 +08:00
|
|
|
as.activityQueueService.Send(ctx, &schema.ActivityMsg{
|
2022-11-22 19:48:27 +08:00
|
|
|
UserID: insertData.UserID,
|
|
|
|
ObjectID: insertData.ID,
|
|
|
|
OriginalObjectID: questionInfo.ID,
|
|
|
|
ActivityTypeKey: constant.ActQuestionAnswered,
|
2022-11-21 19:28:02 +08:00
|
|
|
})
|
2022-09-27 17:59:05 +08:00
|
|
|
return insertData.ID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq) (string, error) {
|
2022-11-24 18:05:56 +08:00
|
|
|
//req.NoNeedReview //true 不需要审核
|
2022-11-24 15:02:31 +08:00
|
|
|
var canUpdate bool
|
|
|
|
_, existUnreviewed, err := as.revisionService.ExistUnreviewedByObjectID(ctx, req.ID)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if existUnreviewed {
|
|
|
|
err = errors.BadRequest(reason.AnswerCannotUpdate)
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2022-11-01 15:25:44 +08:00
|
|
|
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if !exist {
|
|
|
|
return "", errors.BadRequest(reason.QuestionNotFound)
|
|
|
|
}
|
2022-11-24 15:02:31 +08:00
|
|
|
|
|
|
|
answerInfo, exist, err := as.answerRepo.GetByID(ctx, req.ID)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if !exist {
|
|
|
|
return "", nil
|
2022-11-22 16:54:28 +08:00
|
|
|
}
|
|
|
|
|
2023-03-01 17:49:55 +08:00
|
|
|
if answerInfo.Status == entity.AnswerStatusDeleted {
|
|
|
|
err = errors.BadRequest(reason.AnswerCannotUpdate)
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2022-11-28 18:19:22 +08:00
|
|
|
//If the content is the same, ignore it
|
|
|
|
if answerInfo.OriginalText == req.Content {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2022-09-27 17:59:05 +08:00
|
|
|
now := time.Now()
|
|
|
|
insertData := new(entity.Answer)
|
|
|
|
insertData.ID = req.ID
|
2022-12-01 17:38:37 +08:00
|
|
|
insertData.UserID = answerInfo.UserID
|
2022-11-01 15:25:44 +08:00
|
|
|
insertData.QuestionID = req.QuestionID
|
2022-09-27 17:59:05 +08:00
|
|
|
insertData.OriginalText = req.Content
|
2022-11-01 15:25:44 +08:00
|
|
|
insertData.ParsedText = req.HTML
|
2022-09-27 17:59:05 +08:00
|
|
|
insertData.UpdatedAt = now
|
2022-11-24 15:02:31 +08:00
|
|
|
|
2022-12-01 15:26:20 +08:00
|
|
|
insertData.LastEditUserID = "0"
|
|
|
|
if answerInfo.UserID != req.UserID {
|
|
|
|
insertData.LastEditUserID = req.UserID
|
|
|
|
}
|
|
|
|
|
2022-09-27 17:59:05 +08:00
|
|
|
revisionDTO := &schema.AddRevisionDTO{
|
|
|
|
UserID: req.UserID,
|
|
|
|
ObjectID: req.ID,
|
|
|
|
Title: "",
|
|
|
|
Log: req.EditSummary,
|
|
|
|
}
|
2022-11-24 15:02:31 +08:00
|
|
|
|
2022-12-01 15:28:20 +08:00
|
|
|
if req.NoNeedReview || answerInfo.UserID == req.UserID {
|
2022-11-24 18:05:56 +08:00
|
|
|
canUpdate = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if !canUpdate {
|
2022-11-24 15:02:31 +08:00
|
|
|
revisionDTO.Status = entity.RevisionUnreviewedStatus
|
|
|
|
} else {
|
2022-12-01 18:50:45 +08:00
|
|
|
if err = as.answerRepo.UpdateAnswer(ctx, insertData, []string{"original_text", "parsed_text", "updated_at", "last_edit_user_id"}); err != nil {
|
2022-11-24 15:02:31 +08:00
|
|
|
return "", err
|
|
|
|
}
|
2023-02-24 17:10:43 +08:00
|
|
|
err = as.questionCommon.UpdatePostTime(ctx, req.QuestionID)
|
2022-11-24 15:02:31 +08:00
|
|
|
if err != nil {
|
|
|
|
return insertData.ID, err
|
|
|
|
}
|
|
|
|
as.notificationUpdateAnswer(ctx, questionInfo.UserID, insertData.ID, req.UserID)
|
|
|
|
revisionDTO.Status = entity.RevisionReviewPassStatus
|
|
|
|
}
|
|
|
|
|
2022-11-01 15:25:44 +08:00
|
|
|
infoJSON, _ := json.Marshal(insertData)
|
|
|
|
revisionDTO.Content = string(infoJSON)
|
2022-11-22 19:48:27 +08:00
|
|
|
revisionID, err := as.revisionService.AddRevision(ctx, revisionDTO, true)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return insertData.ID, err
|
|
|
|
}
|
2022-11-24 15:02:31 +08:00
|
|
|
if canUpdate {
|
2023-06-05 14:10:04 +08:00
|
|
|
as.activityQueueService.Send(ctx, &schema.ActivityMsg{
|
2022-11-24 15:02:31 +08:00
|
|
|
UserID: insertData.UserID,
|
|
|
|
ObjectID: insertData.ID,
|
|
|
|
OriginalObjectID: insertData.ID,
|
|
|
|
ActivityTypeKey: constant.ActAnswerEdited,
|
|
|
|
RevisionID: revisionID,
|
|
|
|
})
|
|
|
|
}
|
2022-11-22 19:48:27 +08:00
|
|
|
|
2022-09-27 17:59:05 +08:00
|
|
|
return insertData.ID, nil
|
|
|
|
}
|
|
|
|
|
2022-12-22 11:04:19 +08:00
|
|
|
// UpdateAccepted
|
|
|
|
func (as *AnswerService) UpdateAccepted(ctx context.Context, req *schema.AnswerAcceptedReq) error {
|
2022-09-28 10:53:09 +08:00
|
|
|
if req.AnswerID == "" {
|
|
|
|
req.AnswerID = "0"
|
|
|
|
}
|
2022-09-27 17:59:05 +08:00
|
|
|
if req.UserID == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-28 10:53:09 +08:00
|
|
|
newAnswerInfo := &entity.Answer{}
|
|
|
|
newAnswerInfoexist := false
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if req.AnswerID != "0" {
|
|
|
|
newAnswerInfo, newAnswerInfoexist, err = as.answerRepo.GetByID(ctx, req.AnswerID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-03-24 15:12:23 +08:00
|
|
|
newAnswerInfo.ID = uid.DeShortID(newAnswerInfo.ID)
|
2022-09-28 10:53:09 +08:00
|
|
|
if !newAnswerInfoexist {
|
|
|
|
return errors.BadRequest(reason.AnswerNotFound)
|
|
|
|
}
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-03-24 15:12:23 +08:00
|
|
|
questionInfo.ID = uid.DeShortID(questionInfo.ID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if !exist {
|
|
|
|
return errors.BadRequest(reason.QuestionNotFound)
|
|
|
|
}
|
2023-03-24 15:12:23 +08:00
|
|
|
// if questionInfo.UserID != req.UserID {
|
|
|
|
// return fmt.Errorf("no permission to set answer")
|
|
|
|
// }
|
2022-09-27 17:59:05 +08:00
|
|
|
if questionInfo.AcceptedAnswerID == req.AnswerID {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var oldAnswerInfo *entity.Answer
|
|
|
|
if len(questionInfo.AcceptedAnswerID) > 0 && questionInfo.AcceptedAnswerID != "0" {
|
2022-11-01 15:25:44 +08:00
|
|
|
oldAnswerInfo, _, err = as.answerRepo.GetByID(ctx, questionInfo.AcceptedAnswerID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-03-24 15:12:23 +08:00
|
|
|
oldAnswerInfo.ID = uid.DeShortID(oldAnswerInfo.ID)
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
2022-12-22 11:04:19 +08:00
|
|
|
err = as.answerRepo.UpdateAccepted(ctx, req.AnswerID, req.QuestionID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = as.questionCommon.UpdateAccepted(ctx, req.QuestionID, req.AnswerID)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("UpdateLastAnswer error", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
as.updateAnswerRank(ctx, req.UserID, questionInfo, newAnswerInfo, oldAnswerInfo)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (as *AnswerService) updateAnswerRank(ctx context.Context, userID string,
|
2022-11-01 15:25:44 +08:00
|
|
|
questionInfo *entity.Question, newAnswerInfo *entity.Answer, oldAnswerInfo *entity.Answer,
|
|
|
|
) {
|
2022-09-27 17:59:05 +08:00
|
|
|
// if this question is already been answered, should cancel old answer rank
|
|
|
|
if oldAnswerInfo != nil {
|
2023-07-20 19:20:40 +08:00
|
|
|
err := as.answerActivityService.CancelAcceptAnswer(ctx, userID,
|
|
|
|
questionInfo.AcceptedAnswerID, questionInfo.ID, questionInfo.UserID, oldAnswerInfo.UserID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
}
|
|
|
|
}
|
2022-09-28 10:53:09 +08:00
|
|
|
if newAnswerInfo.ID != "" {
|
2023-07-20 19:20:40 +08:00
|
|
|
err := as.answerActivityService.AcceptAnswer(ctx, userID, newAnswerInfo.ID,
|
|
|
|
questionInfo.ID, questionInfo.UserID, newAnswerInfo.UserID, newAnswerInfo.UserID == userID)
|
2022-09-28 10:53:09 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
}
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-01 15:25:44 +08:00
|
|
|
func (as *AnswerService) Get(ctx context.Context, answerID, loginUserID string) (*schema.AnswerInfo, *schema.QuestionInfo, bool, error) {
|
2022-09-27 17:59:05 +08:00
|
|
|
answerInfo, has, err := as.answerRepo.GetByID(ctx, answerID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, has, err
|
|
|
|
}
|
|
|
|
info := as.ShowFormat(ctx, answerInfo)
|
2022-11-01 15:25:44 +08:00
|
|
|
// todo questionFunc
|
|
|
|
questionInfo, err := as.questionCommon.Info(ctx, answerInfo.QuestionID, loginUserID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, has, err
|
|
|
|
}
|
2022-11-01 15:25:44 +08:00
|
|
|
// todo UserFunc
|
2022-12-01 18:36:20 +08:00
|
|
|
|
|
|
|
userIds := make([]string, 0)
|
|
|
|
userIds = append(userIds, answerInfo.UserID)
|
|
|
|
userIds = append(userIds, answerInfo.LastEditUserID)
|
|
|
|
userInfoMap, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIds)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, has, err
|
|
|
|
}
|
2022-12-01 18:36:20 +08:00
|
|
|
|
|
|
|
_, ok := userInfoMap[answerInfo.UserID]
|
|
|
|
if ok {
|
|
|
|
info.UserInfo = userInfoMap[answerInfo.UserID]
|
|
|
|
}
|
|
|
|
_, ok = userInfoMap[answerInfo.LastEditUserID]
|
|
|
|
if ok {
|
|
|
|
info.UpdateUserInfo = userInfoMap[answerInfo.LastEditUserID]
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
2022-11-01 15:25:44 +08:00
|
|
|
if loginUserID == "" {
|
2022-09-27 17:59:05 +08:00
|
|
|
return info, questionInfo, has, nil
|
|
|
|
}
|
|
|
|
|
2022-11-01 15:25:44 +08:00
|
|
|
info.VoteStatus = as.voteRepo.GetVoteStatus(ctx, answerID, loginUserID)
|
2022-09-27 17:59:05 +08:00
|
|
|
|
2022-11-01 15:25:44 +08:00
|
|
|
CollectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserID, []string{answerInfo.ID})
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error("CollectionFunc.SearchObjectCollected error", err)
|
|
|
|
}
|
2022-12-01 18:36:20 +08:00
|
|
|
_, ok = CollectedMap[answerInfo.ID]
|
2022-09-27 17:59:05 +08:00
|
|
|
if ok {
|
|
|
|
info.Collected = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return info, questionInfo, has, nil
|
|
|
|
}
|
|
|
|
|
2022-11-23 17:02:44 +08:00
|
|
|
func (as *AnswerService) AdminSetAnswerStatus(ctx context.Context, req *schema.AdminSetAnswerStatusRequest) error {
|
2022-12-21 16:55:16 +08:00
|
|
|
setStatus, ok := entity.AdminAnswerSearchStatus[req.StatusStr]
|
2022-09-27 17:59:05 +08:00
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("question status does not exist")
|
|
|
|
}
|
2022-11-23 17:02:44 +08:00
|
|
|
answerInfo, exist, err := as.answerRepo.GetAnswer(ctx, req.AnswerID)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !exist {
|
|
|
|
return fmt.Errorf("answer does not exist")
|
|
|
|
}
|
|
|
|
answerInfo.Status = setStatus
|
|
|
|
err = as.answerRepo.UpdateAnswerStatus(ctx, answerInfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if setStatus == entity.AnswerStatusDeleted {
|
2023-05-22 15:26:20 +08:00
|
|
|
// #2372 In order to simplify the process and complexity, as well as to consider if it is in-house,
|
|
|
|
// facing the problem of recovery.
|
|
|
|
//err = as.answerActivityService.DeleteAnswer(ctx, answerInfo.ID, answerInfo.CreatedAt, answerInfo.VoteCount)
|
|
|
|
//if err != nil {
|
|
|
|
// log.Errorf("admin delete question then rank rollback error %s", err.Error())
|
|
|
|
//}
|
2023-06-05 14:10:04 +08:00
|
|
|
as.activityQueueService.Send(ctx, &schema.ActivityMsg{
|
2023-05-22 15:26:20 +08:00
|
|
|
UserID: req.UserID,
|
|
|
|
ObjectID: answerInfo.ID,
|
|
|
|
OriginalObjectID: answerInfo.ID,
|
|
|
|
ActivityTypeKey: constant.ActAnswerDeleted,
|
|
|
|
})
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
msg := &schema.NotificationMsg{}
|
|
|
|
msg.ObjectID = answerInfo.ID
|
|
|
|
msg.Type = schema.NotificationTypeInbox
|
|
|
|
msg.ReceiverUserID = answerInfo.UserID
|
|
|
|
msg.TriggerUserID = answerInfo.UserID
|
|
|
|
msg.ObjectType = constant.AnswerObjectType
|
2023-05-08 15:55:35 +08:00
|
|
|
msg.NotificationAction = constant.NotificationYourAnswerWasDeleted
|
2023-06-02 18:15:54 +08:00
|
|
|
as.notificationQueueService.Send(ctx, msg)
|
2022-09-27 17:59:05 +08:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-11-25 12:07:56 +08:00
|
|
|
func (as *AnswerService) SearchList(ctx context.Context, req *schema.AnswerListReq) ([]*schema.AnswerInfo, int64, error) {
|
2022-09-27 17:59:05 +08:00
|
|
|
list := make([]*schema.AnswerInfo, 0)
|
|
|
|
dbSearch := entity.AnswerSearch{}
|
2022-11-25 12:07:56 +08:00
|
|
|
dbSearch.QuestionID = req.QuestionID
|
|
|
|
dbSearch.Page = req.Page
|
|
|
|
dbSearch.PageSize = req.PageSize
|
|
|
|
dbSearch.Order = req.Order
|
2023-03-01 17:28:16 +08:00
|
|
|
dbSearch.IncludeDeleted = req.CanDelete
|
2023-03-06 12:27:04 +08:00
|
|
|
dbSearch.LoginUserID = req.UserID
|
2022-11-25 12:07:56 +08:00
|
|
|
answerOriginalList, count, err := as.answerRepo.SearchList(ctx, &dbSearch)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return list, count, err
|
|
|
|
}
|
2022-11-25 12:07:56 +08:00
|
|
|
answerList, err := as.SearchFormatInfo(ctx, answerOriginalList, req)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
2022-11-25 12:07:56 +08:00
|
|
|
return answerList, count, err
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
2022-11-25 12:07:56 +08:00
|
|
|
return answerList, count, nil
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
2022-11-25 12:07:56 +08:00
|
|
|
func (as *AnswerService) SearchFormatInfo(ctx context.Context, answers []*entity.Answer, req *schema.AnswerListReq) (
|
|
|
|
[]*schema.AnswerInfo, error) {
|
2022-09-27 17:59:05 +08:00
|
|
|
list := make([]*schema.AnswerInfo, 0)
|
2022-11-25 12:07:56 +08:00
|
|
|
objectIDs := make([]string, 0)
|
|
|
|
userIDs := make([]string, 0)
|
|
|
|
for _, info := range answers {
|
|
|
|
item := as.ShowFormat(ctx, info)
|
2022-09-27 17:59:05 +08:00
|
|
|
list = append(list, item)
|
2022-11-25 12:07:56 +08:00
|
|
|
objectIDs = append(objectIDs, info.ID)
|
|
|
|
userIDs = append(userIDs, info.UserID)
|
2022-12-01 15:26:20 +08:00
|
|
|
userIDs = append(userIDs, info.LastEditUserID)
|
2022-11-25 12:07:56 +08:00
|
|
|
if req.UserID != "" {
|
2023-06-05 14:55:20 +08:00
|
|
|
item.ID = uid.DeShortID(item.ID)
|
2022-11-25 12:07:56 +08:00
|
|
|
item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, req.UserID)
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
}
|
2022-11-25 12:07:56 +08:00
|
|
|
userInfoMap, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIDs)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return list, err
|
|
|
|
}
|
|
|
|
for _, item := range list {
|
2022-11-01 15:25:44 +08:00
|
|
|
_, ok := userInfoMap[item.UserID]
|
2022-09-27 17:59:05 +08:00
|
|
|
if ok {
|
2022-11-01 15:25:44 +08:00
|
|
|
item.UserInfo = userInfoMap[item.UserID]
|
2022-12-01 15:26:20 +08:00
|
|
|
}
|
|
|
|
_, ok = userInfoMap[item.UpdateUserID]
|
|
|
|
if ok {
|
|
|
|
item.UpdateUserInfo = userInfoMap[item.UpdateUserID]
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-25 12:07:56 +08:00
|
|
|
if req.UserID == "" {
|
2022-09-27 17:59:05 +08:00
|
|
|
return list, nil
|
|
|
|
}
|
|
|
|
|
2022-11-25 12:07:56 +08:00
|
|
|
searchObjectCollected, err := as.collectionCommon.SearchObjectCollected(ctx, req.UserID, objectIDs)
|
2022-09-27 17:59:05 +08:00
|
|
|
if err != nil {
|
2022-11-25 12:07:56 +08:00
|
|
|
return nil, err
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range list {
|
2022-11-25 12:07:56 +08:00
|
|
|
_, ok := searchObjectCollected[item.ID]
|
2022-09-27 17:59:05 +08:00
|
|
|
if ok {
|
|
|
|
item.Collected = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range list {
|
2023-06-12 14:56:26 +08:00
|
|
|
item.ID = uid.EnShortID(item.ID)
|
2022-11-25 12:07:56 +08:00
|
|
|
item.MemberActions = permission.GetAnswerPermission(ctx, req.UserID, item.UserID, req.CanEdit, req.CanDelete)
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
return list, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (as *AnswerService) ShowFormat(ctx context.Context, data *entity.Answer) *schema.AnswerInfo {
|
|
|
|
return as.AnswerCommon.ShowFormat(ctx, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (as *AnswerService) notificationUpdateAnswer(ctx context.Context, questionUserID, answerID, answerUserID string) {
|
|
|
|
msg := &schema.NotificationMsg{
|
|
|
|
TriggerUserID: answerUserID,
|
|
|
|
ReceiverUserID: questionUserID,
|
|
|
|
Type: schema.NotificationTypeInbox,
|
|
|
|
ObjectID: answerID,
|
|
|
|
}
|
2022-09-28 14:53:05 +08:00
|
|
|
msg.ObjectType = constant.AnswerObjectType
|
2023-05-08 15:55:35 +08:00
|
|
|
msg.NotificationAction = constant.NotificationUpdateAnswer
|
2023-06-02 18:15:54 +08:00
|
|
|
as.notificationQueueService.Send(ctx, msg)
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|
|
|
|
|
2022-12-27 17:42:23 +08:00
|
|
|
func (as *AnswerService) 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
|
|
|
|
}
|
2022-09-27 17:59:05 +08:00
|
|
|
msg := &schema.NotificationMsg{
|
|
|
|
TriggerUserID: answerUserID,
|
|
|
|
ReceiverUserID: questionUserID,
|
|
|
|
Type: schema.NotificationTypeInbox,
|
|
|
|
ObjectID: answerID,
|
|
|
|
}
|
2022-09-28 14:53:05 +08:00
|
|
|
msg.ObjectType = constant.AnswerObjectType
|
2023-05-08 15:55:35 +08:00
|
|
|
msg.NotificationAction = constant.NotificationAnswerTheQuestion
|
2023-06-02 18:15:54 +08:00
|
|
|
as.notificationQueueService.Send(ctx, msg)
|
2022-12-27 17:42:23 +08:00
|
|
|
|
2023-08-22 11:59:33 +08:00
|
|
|
receiverUserInfo, exist, err := as.userRepo.GetByUserID(ctx, questionUserID)
|
2022-12-27 17:42:23 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !exist {
|
|
|
|
log.Warnf("user %s not found", questionUserID)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-08-22 11:59:33 +08:00
|
|
|
externalNotificationMsg := &schema.ExternalNotificationMsg{
|
|
|
|
ReceiverUserID: receiverUserInfo.ID,
|
|
|
|
ReceiverEmail: receiverUserInfo.EMail,
|
|
|
|
ReceiverLang: receiverUserInfo.Language,
|
|
|
|
}
|
2022-12-27 17:42:23 +08:00
|
|
|
rawData := &schema.NewAnswerTemplateRawData{
|
|
|
|
QuestionTitle: questionTitle,
|
|
|
|
QuestionID: questionID,
|
|
|
|
AnswerID: answerID,
|
|
|
|
AnswerSummary: answerSummary,
|
2023-08-22 14:48:40 +08:00
|
|
|
UnsubscribeCode: token.GenerateToken(),
|
2022-12-27 17:42:23 +08:00
|
|
|
}
|
|
|
|
answerUser, _, _ := as.userCommon.GetUserBasicInfoByID(ctx, answerUserID)
|
|
|
|
if answerUser != nil {
|
|
|
|
rawData.AnswerUserDisplayName = answerUser.DisplayName
|
|
|
|
}
|
2023-08-22 11:59:33 +08:00
|
|
|
externalNotificationMsg.NewAnswerTemplateRawData = rawData
|
|
|
|
as.externalNotificationQueueService.Send(ctx, externalNotificationMsg)
|
2022-09-27 17:59:05 +08:00
|
|
|
}
|