diff --git a/internal/base/constant/acticity.go b/internal/base/constant/acticity.go index 128659c4..a1513338 100644 --- a/internal/base/constant/acticity.go +++ b/internal/base/constant/acticity.go @@ -5,8 +5,15 @@ package constant type ActivityTypeKey string const ( - ActEdited = "edited" - ActClosed = "closed" + ActEdited = "edited" + ActClosed = "closed" + ActAccepted = "accepted" + ActVotedDown = "voted_down" + ActVotedUp = "voted_up" + ActVoteDown = "vote_down" + ActVoteUp = "vote_up" + ActUpVote = "upvote" + ActDownVote = "downvote" ) const ( @@ -17,7 +24,7 @@ const ( ActQuestionCommented ActivityTypeKey = "question.commented" ActQuestionAccept ActivityTypeKey = "question.accept" ActQuestionUpvote ActivityTypeKey = "question.upvote" - ActQuestionDownvote ActivityTypeKey = "question.downvote" + ActQuestionDownVote ActivityTypeKey = "question.downvote" ActQuestionEdited ActivityTypeKey = "question.edited" ActQuestionRollback ActivityTypeKey = "question.rollback" ActQuestionDeleted ActivityTypeKey = "question.deleted" @@ -29,7 +36,7 @@ const ( ActAnswerCommented ActivityTypeKey = "answer.commented" ActAnswerAccept ActivityTypeKey = "answer.accept" ActAnswerUpvote ActivityTypeKey = "answer.upvote" - ActAnswerDownvote ActivityTypeKey = "answer.downvote" + ActAnswerDownVote ActivityTypeKey = "answer.downvote" ActAnswerEdited ActivityTypeKey = "answer.edited" ActAnswerRollback ActivityTypeKey = "answer.rollback" ActAnswerDeleted ActivityTypeKey = "answer.deleted" diff --git a/internal/controller/activity_controller.go b/internal/controller/activity_controller.go index 25a00d6b..ed33a56f 100644 --- a/internal/controller/activity_controller.go +++ b/internal/controller/activity_controller.go @@ -39,6 +39,9 @@ func (ac *ActivityController) GetObjectTimeline(ctx *gin.Context) { } req.UserID = middleware.GetLoginUserIDFromContext(ctx) + if userInfo := middleware.GetUserInfoFromContext(ctx); userInfo != nil { + req.IsAdmin = userInfo.IsAdmin + } resp, err := ac.activityService.GetObjectTimeline(ctx, req) handler.HandleResponse(ctx, err, resp) diff --git a/internal/repo/activity/activity_repo.go b/internal/repo/activity/activity_repo.go index ddd09d1b..3f8ad4b2 100644 --- a/internal/repo/activity/activity_repo.go +++ b/internal/repo/activity/activity_repo.go @@ -2,10 +2,13 @@ package activity import ( "context" + "fmt" + "github.com/answerdev/answer/internal/base/constant" "github.com/answerdev/answer/internal/base/data" "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/entity" + "github.com/answerdev/answer/internal/repo/config" "github.com/answerdev/answer/internal/service/activity" "github.com/segmentfault/pacman/errors" ) @@ -27,7 +30,22 @@ func NewActivityRepo( func (ar *activityRepo) GetObjectAllActivity(ctx context.Context, objectID string, showVote bool) ( activityList []*entity.Activity, err error) { activityList = make([]*entity.Activity, 0) - session := ar.data.DB.Desc("created_at") // TODO: if showVote is false do not show vote activity + session := ar.data.DB.Desc("created_at") + + if !showVote { + var activityTypeNotShown []int + for _, obj := range []string{constant.AnswerObjectType, constant.QuestionObjectType, constant.CommentObjectType} { + for _, act := range []string{ + constant.ActVotedDown, + constant.ActVotedUp, + constant.ActVoteDown, + constant.ActVoteUp, + } { + activityTypeNotShown = append(activityTypeNotShown, config.Key2IDMapping[fmt.Sprintf("%s.%s", obj, act)]) + } + } + session.NotIn("activity_type", activityTypeNotShown) + } err = session.Find(&activityList, &entity.Activity{OriginalObjectID: objectID}) if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() diff --git a/internal/repo/activity/answer_repo.go b/internal/repo/activity/answer_repo.go index 1c47c8e1..74d9136d 100644 --- a/internal/repo/activity/answer_repo.go +++ b/internal/repo/activity/answer_repo.go @@ -144,9 +144,11 @@ func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context, if action == acceptAction { addActivity.UserID = questionUserID addActivity.TriggerUserID = converter.StringToInt64(answerUserID) + addActivity.OriginalObjectID = questionObjID // if activity is 'accept' means this question is accept the answer. } else { addActivity.UserID = answerUserID addActivity.TriggerUserID = converter.StringToInt64(answerUserID) + addActivity.OriginalObjectID = answerObjID // if activity is 'accepted' means this answer was accepted. } if isSelf { addActivity.Rank = 0 @@ -234,16 +236,17 @@ func (ar *AnswerActivityRepo) CancelAcceptAnswer(ctx context.Context, return errors.InternalServer(reason.DatabaseError).WithError(e).WithStack() } addActivity := &entity.Activity{ - ObjectID: answerObjID, - OriginalObjectID: questionObjID, - ActivityType: activityType, - Rank: -deltaRank, - HasRank: hasRank, + ObjectID: answerObjID, + ActivityType: activityType, + Rank: -deltaRank, + HasRank: hasRank, } if action == acceptAction { addActivity.UserID = questionUserID + addActivity.OriginalObjectID = questionObjID } else { addActivity.UserID = answerUserID + addActivity.OriginalObjectID = answerObjID } addActivityList = append(addActivityList, addActivity) } diff --git a/internal/schema/activity.go b/internal/schema/activity.go index c0598c30..d040b445 100644 --- a/internal/schema/activity.go +++ b/internal/schema/activity.go @@ -17,6 +17,7 @@ type GetObjectTimelineReq struct { ObjectID string `validate:"omitempty,gt=0,lte=100" form:"object_id"` ShowVote bool `validate:"omitempty" form:"show_vote"` UserID string `json:"-"` + IsAdmin bool `json:"-"` } // GetObjectTimelineResp get object timeline response @@ -42,10 +43,11 @@ type ActObjectTimeline struct { // ActObjectInfo act object info type ActObjectInfo struct { - Title string `json:"title"` - ObjectType string `json:"object_type"` - QuestionID string `json:"question_id"` - AnswerID string `json:"answer_id"` + ObjectType string `json:"object_type"` + Title string `json:"title"` + QuestionID string `json:"question_id"` + AnswerID string `json:"answer_id"` + MainTagSlugName string `json:"main_tag_slug_name"` } // GetObjectTimelineDetailReq get object timeline detail request @@ -63,9 +65,11 @@ type GetObjectTimelineDetailResp struct { // ObjectTimelineDetail object timeline detail type ObjectTimelineDetail struct { - Title string `json:"title"` - Tags []*ObjectTimelineTag `json:"tags"` - OriginalText string `json:"original_text"` + Title string `json:"title"` + Tags []*ObjectTimelineTag `json:"tags"` + OriginalText string `json:"original_text"` + SlugName string `json:"slug_name"` + MainTagSlugName string `json:"main_tag_slug_name"` } // ObjectTimelineTag object timeline tags diff --git a/internal/service/activity/activity.go b/internal/service/activity/activity.go index b5af4c9e..e8b624bc 100644 --- a/internal/service/activity/activity.go +++ b/internal/service/activity/activity.go @@ -17,6 +17,7 @@ import ( "github.com/answerdev/answer/internal/service/tag_common" usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/answerdev/answer/pkg/converter" + "github.com/answerdev/answer/pkg/obj" "github.com/segmentfault/pacman/log" ) @@ -73,6 +74,14 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge return nil, err } resp.ObjectInfo.Title = objInfo.Title + if objInfo.ObjectType == constant.TagObjectType { + tag, exist, _ := as.tagCommonService.GetTagByID(ctx, objInfo.TagID) + if exist { + resp.ObjectInfo.Title = tag.SlugName + resp.ObjectInfo.MainTagSlugName = tag.MainTagSlugName + } + } + resp.ObjectInfo.ObjectType = objInfo.ObjectType resp.ObjectInfo.QuestionID = objInfo.QuestionID resp.ObjectInfo.AnswerID = objInfo.AnswerID @@ -89,13 +98,14 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge Cancelled: act.Cancelled == entity.ActivityCancelled, ObjectID: act.ObjectID, } + item.ObjectType, _ = obj.GetObjectTypeStrByObjectID(act.ObjectID) if item.Cancelled { item.CancelledAt = act.CancelledAt.Unix() } // database save activity type is number, change to activity type string is like "question.asked". // so we need to cut the front part of '.' - item.ObjectType, item.ActivityType, _ = strings.Cut(config.ID2KeyMapping[act.ActivityType], ".") + _, item.ActivityType, _ = strings.Cut(config.ID2KeyMapping[act.ActivityType], ".") isHidden, formattedActivityType := formatActivity(item.ActivityType) if isHidden { @@ -103,14 +113,20 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge } item.ActivityType = formattedActivityType - // get user info - userBasicInfo, exist, err := as.userCommon.GetUserBasicInfoByID(ctx, act.UserID) - if err != nil { - return nil, err - } - if exist { - item.Username = userBasicInfo.Username - item.UserDisplayName = userBasicInfo.DisplayName + // if activity is down vote, only admin can see who does it. + if item.ActivityType == constant.ActDownVote && !req.IsAdmin { + item.Username = "N/A" + item.UserDisplayName = "N/A" + } else { + // get user info + userBasicInfo, exist, err := as.userCommon.GetUserBasicInfoByID(ctx, act.UserID) + if err != nil { + return nil, err + } + if exist { + item.Username = userBasicInfo.Username + item.UserDisplayName = userBasicInfo.DisplayName + } } if item.ObjectType == constant.CommentObjectType { @@ -156,8 +172,14 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st resp *schema.ObjectTimelineDetail, err error) { resp = &schema.ObjectTimelineDetail{Tags: make([]*schema.ObjectTimelineTag, 0)} + // if request revision is 0, return null object detail. + if revisionID == "0" { + return nil, nil + } + revision, err := as.revisionService.GetRevision(ctx, revisionID) if err != nil { + log.Warn(err) return nil, nil } objInfo, err := as.objectInfoService.GetInfo(ctx, revision.ObjectID) @@ -199,6 +221,8 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st } resp.Title = data.SlugName resp.OriginalText = data.OriginalText + resp.SlugName = data.SlugName + resp.MainTagSlugName = data.MainTagSlugName default: log.Errorf("unknown object type %s", objInfo.ObjectType) } @@ -206,14 +230,15 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st } func formatActivity(activityType string) (isHidden bool, formattedActivityType string) { - if activityType == "voted_up" || activityType == "voted_down" || activityType == "accepted" { + if activityType == constant.ActVotedUp || + activityType == constant.ActVotedDown { return true, "" } - if activityType == "vote_up" { - return false, "upvote" + if activityType == constant.ActVoteUp { + return false, constant.ActUpVote } - if activityType == "vote_down" { - return false, "downvote" + if activityType == constant.ActVoteDown { + return false, constant.ActDownVote } return false, activityType }