From d399b9756ff5eca9dc99d295c6aa921752d41ba5 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Wed, 23 Nov 2022 14:21:11 +0800 Subject: [PATCH 01/14] feat: activity timeline revision add tag detail --- cmd/answer/wire_gen.go | 2 +- internal/base/constant/acticity.go | 9 ++-- internal/entity/question_entity.go | 15 ++++--- internal/repo/activity/activity_repo.go | 3 +- internal/schema/activity.go | 15 +++++-- internal/service/activity/activity.go | 43 +++++++++++++------ .../revision_common/revision_service.go | 2 + 7 files changed, 61 insertions(+), 28 deletions(-) diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index 21b1022e..3bab5d81 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -189,7 +189,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, activityCommon := activity_common2.NewActivityCommon(activityRepo) activityActivityRepo := activity.NewActivityRepo(dataData) commentCommonService := comment_common.NewCommentCommonService(commentCommonRepo) - activityService := activity2.NewActivityService(activityActivityRepo, userCommon, activityCommon, tagCommonService, objService, commentCommonService, revisionService) + activityService := activity2.NewActivityService(activityActivityRepo, userCommon, activityCommon, tagCommonService, objService, commentCommonService, revisionService, metaService) activityController := controller.NewActivityController(activityCommon, activityService) answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, controller_backyardReportController, userBackyardController, reasonController, themeController, siteInfoController, siteinfoController, notificationController, dashboardController, uploadController, activityController) swaggerRouter := router.NewSwaggerRouter(swaggerConf) diff --git a/internal/base/constant/acticity.go b/internal/base/constant/acticity.go index befa016b..128659c4 100644 --- a/internal/base/constant/acticity.go +++ b/internal/base/constant/acticity.go @@ -4,6 +4,11 @@ package constant type ActivityTypeKey string +const ( + ActEdited = "edited" + ActClosed = "closed" +) + const ( ActQuestionAsked ActivityTypeKey = "question.asked" ActQuestionClosed ActivityTypeKey = "question.closed" @@ -19,8 +24,6 @@ const ( ActQuestionUndeleted ActivityTypeKey = "question.undeleted" ) -// answer activity - const ( ActAnswerAnswered ActivityTypeKey = "answer.answered" ActAnswerCommented ActivityTypeKey = "answer.commented" @@ -33,8 +36,6 @@ const ( ActAnswerUndeleted ActivityTypeKey = "answer.undeleted" ) -// tag activity - const ( ActTagCreated ActivityTypeKey = "tag.created" ActTagEdited ActivityTypeKey = "tag.edited" diff --git a/internal/entity/question_entity.go b/internal/entity/question_entity.go index fd459e71..7d1fd167 100644 --- a/internal/entity/question_entity.go +++ b/internal/entity/question_entity.go @@ -62,11 +62,12 @@ type QuestionWithTagsRevision struct { // TagSimpleInfoForRevision tag simple info for revision type TagSimpleInfoForRevision struct { - ID string `xorm:"not null pk comment('tag_id') BIGINT(20) id"` - MainTagID int64 `xorm:"not null default 0 BIGINT(20) main_tag_id"` - SlugName string `xorm:"not null default '' unique VARCHAR(35) slug_name"` - DisplayName string `xorm:"not null default '' VARCHAR(35) display_name"` - Recommend bool `xorm:"not null default false BOOL recommend"` - Reserved bool `xorm:"not null default false BOOL reserved"` - RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"` + ID string `xorm:"not null pk comment('tag_id') BIGINT(20) id"` + MainTagID int64 `xorm:"not null default 0 BIGINT(20) main_tag_id"` + MainTagSlugName string `xorm:"not null default '' VARCHAR(35) main_tag_slug_name"` + SlugName string `xorm:"not null default '' unique VARCHAR(35) slug_name"` + DisplayName string `xorm:"not null default '' VARCHAR(35) display_name"` + Recommend bool `xorm:"not null default false BOOL recommend"` + Reserved bool `xorm:"not null default false BOOL reserved"` + RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"` } diff --git a/internal/repo/activity/activity_repo.go b/internal/repo/activity/activity_repo.go index e192bd16..ddd09d1b 100644 --- a/internal/repo/activity/activity_repo.go +++ b/internal/repo/activity/activity_repo.go @@ -27,7 +27,8 @@ func NewActivityRepo( func (ar *activityRepo) GetObjectAllActivity(ctx context.Context, objectID string, showVote bool) ( activityList []*entity.Activity, err error) { activityList = make([]*entity.Activity, 0) - err = ar.data.DB.Find(&activityList, &entity.Activity{OriginalObjectID: objectID}) + session := ar.data.DB.Desc("created_at") // TODO: if showVote is false do not show vote activity + err = session.Find(&activityList, &entity.Activity{OriginalObjectID: objectID}) if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } diff --git a/internal/schema/activity.go b/internal/schema/activity.go index 196e9198..c0598c30 100644 --- a/internal/schema/activity.go +++ b/internal/schema/activity.go @@ -63,7 +63,16 @@ type GetObjectTimelineDetailResp struct { // ObjectTimelineDetail object timeline detail type ObjectTimelineDetail struct { - Title string `json:"title"` - Tags []string `json:"tags"` - OriginalText string `json:"original_text"` + Title string `json:"title"` + Tags []*ObjectTimelineTag `json:"tags"` + OriginalText string `json:"original_text"` +} + +// ObjectTimelineTag object timeline tags +type ObjectTimelineTag struct { + SlugName string `json:"slug_name"` + DisplayName string `json:"display_name"` + MainTagSlugName string `json:"main_tag_slug_name"` + Recommend bool `json:"recommend"` + Reserved bool `json:"reserved"` } diff --git a/internal/service/activity/activity.go b/internal/service/activity/activity.go index 73fce527..b5af4c9e 100644 --- a/internal/service/activity/activity.go +++ b/internal/service/activity/activity.go @@ -11,6 +11,7 @@ import ( "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service/activity_common" "github.com/answerdev/answer/internal/service/comment_common" + "github.com/answerdev/answer/internal/service/meta" "github.com/answerdev/answer/internal/service/object_info" "github.com/answerdev/answer/internal/service/revision_common" "github.com/answerdev/answer/internal/service/tag_common" @@ -33,6 +34,7 @@ type ActivityService struct { objectInfoService *object_info.ObjService commentCommonService *comment_common.CommentCommonService revisionService *revision_common.RevisionService + metaService *meta.MetaService } // NewActivityService new activity service @@ -44,6 +46,7 @@ func NewActivityService( objectInfoService *object_info.ObjService, commentCommonService *comment_common.CommentCommonService, revisionService *revision_common.RevisionService, + metaService *meta.MetaService, ) *ActivityService { return &ActivityService{ objectInfoService: objectInfoService, @@ -53,6 +56,7 @@ func NewActivityService( tagCommonService: tagCommonService, commentCommonService: commentCommonService, revisionService: revisionService, + metaService: metaService, } } @@ -116,8 +120,23 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge } else { item.Comment = comment.ParsedText } + } else if item.ActivityType == constant.ActEdited { + revision, err := as.revisionService.GetRevision(ctx, item.RevisionID) + if err != nil { + log.Error(err) + } + item.Comment = revision.Log + } else if item.ActivityType == constant.ActClosed { + metaInfo, err := as.metaService.GetMetaByObjectIdAndKey(ctx, item.ObjectID, entity.QuestionCloseReasonKey) + if err != nil { + log.Error(err) + } else { + closeMsg := &schema.CloseQuestionMeta{} + if err := json.Unmarshal([]byte(metaInfo.Value), closeMsg); err != nil { + item.Comment = closeMsg.CloseMsg + } + } } - resp.Timeline = append(resp.Timeline, item) } return @@ -127,25 +146,19 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge func (as *ActivityService) GetObjectTimelineDetail(ctx context.Context, req *schema.GetObjectTimelineDetailReq) ( resp *schema.GetObjectTimelineDetailResp, err error) { resp = &schema.GetObjectTimelineDetailResp{} - resp.OldRevision, err = as.getOneObjectDetail(ctx, req.OldRevisionID) - if err != nil { - return nil, err - } - resp.NewRevision, err = as.getOneObjectDetail(ctx, req.NewRevisionID) - if err != nil { - return nil, err - } + resp.OldRevision, _ = as.getOneObjectDetail(ctx, req.OldRevisionID) + resp.NewRevision, _ = as.getOneObjectDetail(ctx, req.NewRevisionID) return resp, nil } // GetObjectTimelineDetail get object detail func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID string) ( resp *schema.ObjectTimelineDetail, err error) { - resp = &schema.ObjectTimelineDetail{Tags: make([]string, 0)} + resp = &schema.ObjectTimelineDetail{Tags: make([]*schema.ObjectTimelineTag, 0)} revision, err := as.revisionService.GetRevision(ctx, revisionID) if err != nil { - return nil, err + return nil, nil } objInfo, err := as.objectInfoService.GetInfo(ctx, revision.ObjectID) if err != nil { @@ -160,7 +173,13 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st return resp, nil } for _, tag := range data.Tags { - resp.Tags = append(resp.Tags, tag.SlugName) + resp.Tags = append(resp.Tags, &schema.ObjectTimelineTag{ + SlugName: tag.SlugName, + DisplayName: tag.DisplayName, + MainTagSlugName: tag.MainTagSlugName, + Recommend: tag.Recommend, + Reserved: tag.Reserved, + }) } resp.Title = data.Title resp.OriginalText = data.OriginalText diff --git a/internal/service/revision_common/revision_service.go b/internal/service/revision_common/revision_service.go index d5e08b27..4d85ca98 100644 --- a/internal/service/revision_common/revision_service.go +++ b/internal/service/revision_common/revision_service.go @@ -7,6 +7,7 @@ import ( "github.com/answerdev/answer/internal/service/revision" usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/segmentfault/pacman/errors" + "github.com/segmentfault/pacman/log" "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/schema" @@ -47,6 +48,7 @@ func (rs *RevisionService) GetRevision(ctx context.Context, revisionID string) ( revision *entity.Revision, err error) { revisionInfo, exist, err := rs.revisionRepo.GetRevisionByID(ctx, revisionID) if err != nil { + log.Error(err) return nil, err } if !exist { From ff38dc5a0cf46ec6ec26b951ea589e4bfa79e77a Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Wed, 23 Nov 2022 16:12:48 +0800 Subject: [PATCH 02/14] feat: activity timeline show vote activity --- internal/base/constant/acticity.go | 15 ++++-- internal/controller/activity_controller.go | 3 ++ internal/repo/activity/activity_repo.go | 20 +++++++- internal/repo/activity/answer_repo.go | 13 ++++-- internal/schema/activity.go | 18 +++++--- internal/service/activity/activity.go | 53 ++++++++++++++++------ 6 files changed, 91 insertions(+), 31 deletions(-) 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 } From 60f4264e5840b1fe731320f6da3f6f28d2c85d07 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Wed, 23 Nov 2022 17:02:44 +0800 Subject: [PATCH 03/14] feat: answer and tag delete add activity --- internal/controller/answer_controller.go | 5 ++-- internal/controller/tag_controller.go | 2 +- internal/entity/answer_entity.go | 5 ---- internal/schema/answer_schema.go | 10 +++++-- internal/service/answer_service.go | 30 ++++++++++++++------- internal/service/comment/comment_service.go | 11 ++++++-- internal/service/question_service.go | 8 +++++- internal/service/tag/tag_service.go | 12 ++++++--- 8 files changed, 56 insertions(+), 27 deletions(-) diff --git a/internal/controller/answer_controller.go b/internal/controller/answer_controller.go index 6db77008..1124584f 100644 --- a/internal/controller/answer_controller.go +++ b/internal/controller/answer_controller.go @@ -6,7 +6,6 @@ import ( "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/middleware" "github.com/answerdev/answer/internal/base/reason" - "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service" "github.com/answerdev/answer/internal/service/dashboard" @@ -238,10 +237,10 @@ func (ac *AnswerController) Adopted(ctx *gin.Context) { // @Router /answer/admin/api/answer/status [put] // @Success 200 {object} handler.RespBody func (ac *AnswerController) AdminSetAnswerStatus(ctx *gin.Context) { - req := &entity.AdminSetAnswerStatusRequest{} + req := &schema.AdminSetAnswerStatusRequest{} if handler.BindAndCheck(ctx, req) { return } - err := ac.answerService.AdminSetAnswerStatus(ctx, req.AnswerID, req.StatusStr) + err := ac.answerService.AdminSetAnswerStatus(ctx, req) handler.HandleResponse(ctx, err, gin.H{}) } diff --git a/internal/controller/tag_controller.go b/internal/controller/tag_controller.go index 673a746c..8e88845c 100644 --- a/internal/controller/tag_controller.go +++ b/internal/controller/tag_controller.go @@ -69,7 +69,7 @@ func (tc *TagController) RemoveTag(ctx *gin.Context) { return } - err := tc.tagService.RemoveTag(ctx, req.TagID) + err := tc.tagService.RemoveTag(ctx, req) handler.HandleResponse(ctx, err, nil) } diff --git a/internal/entity/answer_entity.go b/internal/entity/answer_entity.go index c51ae157..33e04d32 100644 --- a/internal/entity/answer_entity.go +++ b/internal/entity/answer_entity.go @@ -48,11 +48,6 @@ type CmsAnswerSearch struct { QuestionID string `validate:"omitempty,gt=0,lte=24" json:"question_id" form:"question_id" ` //Query string } -type AdminSetAnswerStatusRequest struct { - StatusStr string `json:"status" form:"status"` - AnswerID string `json:"answer_id" form:"answer_id"` -} - // TableName answer table name func (Answer) TableName() string { return "answer" diff --git a/internal/schema/answer_schema.go b/internal/schema/answer_schema.go index 0c396865..6eabd6fb 100644 --- a/internal/schema/answer_schema.go +++ b/internal/schema/answer_schema.go @@ -74,7 +74,13 @@ type AdminAnswerInfo struct { } type AnswerAdoptedReq struct { - QuestionID string `json:"question_id" ` // question_id - AnswerID string `json:"answer_id" ` + QuestionID string `json:"question_id"` + AnswerID string `json:"answer_id"` UserID string `json:"-" ` } + +type AdminSetAnswerStatusRequest struct { + StatusStr string `json:"status"` + AnswerID string `json:"answer_id"` + UserID string `json:"-" ` +} diff --git a/internal/service/answer_service.go b/internal/service/answer_service.go index 5a6470a1..d02e3ce7 100644 --- a/internal/service/answer_service.go +++ b/internal/service/answer_service.go @@ -7,19 +7,18 @@ import ( "time" "github.com/answerdev/answer/internal/base/constant" - "github.com/answerdev/answer/internal/service/activity" - "github.com/answerdev/answer/internal/service/activity_common" - "github.com/answerdev/answer/internal/service/activity_queue" - "github.com/answerdev/answer/internal/service/notice_queue" - "github.com/answerdev/answer/internal/service/revision_common" - "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/schema" + "github.com/answerdev/answer/internal/service/activity" + "github.com/answerdev/answer/internal/service/activity_common" + "github.com/answerdev/answer/internal/service/activity_queue" answercommon "github.com/answerdev/answer/internal/service/answer_common" collectioncommon "github.com/answerdev/answer/internal/service/collection_common" + "github.com/answerdev/answer/internal/service/notice_queue" "github.com/answerdev/answer/internal/service/permission" questioncommon "github.com/answerdev/answer/internal/service/question_common" + "github.com/answerdev/answer/internal/service/revision_common" usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/log" @@ -116,6 +115,12 @@ func (as *AnswerService) RemoveAnswer(ctx context.Context, req *schema.RemoveAns if err != nil { log.Errorf("delete answer activity change failed: %s", err.Error()) } + activity_queue.AddActivity(&schema.ActivityMsg{ + UserID: req.UserID, + ObjectID: answerInfo.ID, + OriginalObjectID: answerInfo.ID, + ActivityTypeKey: constant.ActAnswerDeleted, + }) return } @@ -352,12 +357,12 @@ func (as *AnswerService) Get(ctx context.Context, answerID, loginUserID string) return info, questionInfo, has, nil } -func (as *AnswerService) AdminSetAnswerStatus(ctx context.Context, answerID string, setStatusStr string) error { - setStatus, ok := entity.CmsAnswerSearchStatus[setStatusStr] +func (as *AnswerService) AdminSetAnswerStatus(ctx context.Context, req *schema.AdminSetAnswerStatusRequest) error { + setStatus, ok := entity.CmsAnswerSearchStatus[req.StatusStr] if !ok { return fmt.Errorf("question status does not exist") } - answerInfo, exist, err := as.answerRepo.GetAnswer(ctx, answerID) + answerInfo, exist, err := as.answerRepo.GetAnswer(ctx, req.AnswerID) if err != nil { return err } @@ -374,6 +379,13 @@ func (as *AnswerService) AdminSetAnswerStatus(ctx context.Context, answerID stri 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()) + } else { + activity_queue.AddActivity(&schema.ActivityMsg{ + UserID: req.UserID, + ObjectID: answerInfo.ID, + OriginalObjectID: answerInfo.ID, + ActivityTypeKey: constant.ActAnswerDeleted, + }) } } diff --git a/internal/service/comment/comment_service.go b/internal/service/comment/comment_service.go index 236862be..686aee00 100644 --- a/internal/service/comment/comment_service.go +++ b/internal/service/comment/comment_service.go @@ -150,12 +150,19 @@ func (cs *CommentService) AddComment(ctx context.Context, req *schema.AddComment resp.UserStatus = userInfo.Status } - activity_queue.AddActivity(&schema.ActivityMsg{ + activityMsg := &schema.ActivityMsg{ UserID: comment.UserID, ObjectID: comment.ID, OriginalObjectID: req.ObjectID, ActivityTypeKey: constant.ActQuestionCommented, - }) + } + switch objInfo.ObjectType { + case constant.QuestionObjectType: + activityMsg.ActivityTypeKey = constant.ActQuestionCommented + case constant.AnswerObjectType: + activityMsg.ActivityTypeKey = constant.ActAnswerCommented + } + activity_queue.AddActivity(activityMsg) return resp, nil } diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 09811338..01a3876b 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -626,13 +626,19 @@ func (qs *QuestionService) AdminSetQuestionStatus(ctx context.Context, questionI if err != nil { log.Errorf("admin delete question then rank rollback error %s", err.Error()) } + activity_queue.AddActivity(&schema.ActivityMsg{ + UserID: questionInfo.UserID, + ObjectID: questionInfo.ID, + OriginalObjectID: questionInfo.ID, + ActivityTypeKey: constant.ActQuestionDeleted, + }) } if setStatus == entity.QuestionStatusAvailable && questionInfo.Status == entity.QuestionStatusClosed { activity_queue.AddActivity(&schema.ActivityMsg{ UserID: questionInfo.UserID, ObjectID: questionInfo.ID, OriginalObjectID: questionInfo.ID, - ActivityTypeKey: constant.ActQuestionDeleted, + ActivityTypeKey: constant.ActQuestionReopened, }) } if setStatus == entity.QuestionStatusClosed && questionInfo.Status != entity.QuestionStatusClosed { diff --git a/internal/service/tag/tag_service.go b/internal/service/tag/tag_service.go index 8642f32c..99065079 100644 --- a/internal/service/tag/tag_service.go +++ b/internal/service/tag/tag_service.go @@ -56,13 +56,17 @@ func NewTagService( } // RemoveTag delete tag -func (ts *TagService) RemoveTag(ctx context.Context, tagID string) (err error) { - // TODO permission - - err = ts.tagRepo.RemoveTag(ctx, tagID) +func (ts *TagService) RemoveTag(ctx context.Context, req *schema.RemoveTagReq) (err error) { + err = ts.tagRepo.RemoveTag(ctx, req.TagID) if err != nil { return err } + activity_queue.AddActivity(&schema.ActivityMsg{ + UserID: req.UserID, + ObjectID: req.TagID, + OriginalObjectID: req.TagID, + ActivityTypeKey: constant.ActTagDeleted, + }) return nil } From 45bd739f519c6019c32420a2c452c8ddb465ce0c Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Wed, 23 Nov 2022 17:32:59 +0800 Subject: [PATCH 04/14] fix: timeline revision tag detail title is display name --- internal/base/constant/acticity.go | 3 --- internal/service/activity/activity.go | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/base/constant/acticity.go b/internal/base/constant/acticity.go index a1513338..d9b9787f 100644 --- a/internal/base/constant/acticity.go +++ b/internal/base/constant/acticity.go @@ -1,13 +1,10 @@ package constant -// question activity - type ActivityTypeKey string const ( ActEdited = "edited" ActClosed = "closed" - ActAccepted = "accepted" ActVotedDown = "voted_down" ActVotedUp = "voted_up" ActVoteDown = "vote_down" diff --git a/internal/service/activity/activity.go b/internal/service/activity/activity.go index e8b624bc..a7d8f122 100644 --- a/internal/service/activity/activity.go +++ b/internal/service/activity/activity.go @@ -219,7 +219,7 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st log.Errorf("revision parsing error %s", err) return resp, nil } - resp.Title = data.SlugName + resp.Title = data.DisplayName resp.OriginalText = data.OriginalText resp.SlugName = data.SlugName resp.MainTagSlugName = data.MainTagSlugName From 60d6e18cd459ff715809598feff34884acd72bdc Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Wed, 23 Nov 2022 17:58:03 +0800 Subject: [PATCH 05/14] fix: add user id when admin update answer status --- internal/controller/answer_controller.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/controller/answer_controller.go b/internal/controller/answer_controller.go index 1124584f..78bacbaf 100644 --- a/internal/controller/answer_controller.go +++ b/internal/controller/answer_controller.go @@ -241,6 +241,9 @@ func (ac *AnswerController) AdminSetAnswerStatus(ctx *gin.Context) { if handler.BindAndCheck(ctx, req) { return } + + req.UserID = middleware.GetLoginUserIDFromContext(ctx) + err := ac.answerService.AdminSetAnswerStatus(ctx, req) handler.HandleResponse(ctx, err, gin.H{}) } From dbc316c40dac531dd386233147a82dd1e5904e8a Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Wed, 23 Nov 2022 19:30:38 +0800 Subject: [PATCH 06/14] refactor: activity timeline extract user info and comment function --- internal/schema/activity.go | 1 + internal/service/activity/activity.go | 143 +++++++++++++++++--------- 2 files changed, 94 insertions(+), 50 deletions(-) diff --git a/internal/schema/activity.go b/internal/schema/activity.go index d040b445..5765110f 100644 --- a/internal/schema/activity.go +++ b/internal/schema/activity.go @@ -39,6 +39,7 @@ type ActObjectTimeline struct { ObjectType string `json:"object_type"` Cancelled bool `json:"cancelled"` CancelledAt int64 `json:"cancelled_at"` + UserID string `json:"-"` } // ActObjectInfo act object info diff --git a/internal/service/activity/activity.go b/internal/service/activity/activity.go index a7d8f122..ed399b1c 100644 --- a/internal/service/activity/activity.go +++ b/internal/service/activity/activity.go @@ -69,22 +69,10 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge Timeline: make([]*schema.ActObjectTimeline, 0), } - objInfo, err := as.objectInfoService.GetInfo(ctx, req.ObjectID) + resp.ObjectInfo, err = as.getTimelineMainObjInfo(ctx, req.ObjectID) if err != nil { 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 activityList, err := as.activityRepo.GetObjectAllActivity(ctx, req.ObjectID, req.ShowVote) if err != nil { @@ -106,58 +94,113 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge // 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.ActivityType, _ = strings.Cut(config.ID2KeyMapping[act.ActivityType], ".") - - isHidden, formattedActivityType := formatActivity(item.ActivityType) - if isHidden { + // format activity type string to show + if isHidden, formattedActivityType := formatActivity(item.ActivityType); isHidden { continue + } else { + item.ActivityType = formattedActivityType } - item.ActivityType = formattedActivityType // 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 - } + item.UserID = act.UserID } - if item.ObjectType == constant.CommentObjectType { - comment, err := as.commentCommonService.GetComment(ctx, item.ObjectID) - if err != nil { - log.Error(err) - } else { - item.Comment = comment.ParsedText - } - } else if item.ActivityType == constant.ActEdited { - revision, err := as.revisionService.GetRevision(ctx, item.RevisionID) - if err != nil { - log.Error(err) - } - item.Comment = revision.Log - } else if item.ActivityType == constant.ActClosed { - metaInfo, err := as.metaService.GetMetaByObjectIdAndKey(ctx, item.ObjectID, entity.QuestionCloseReasonKey) - if err != nil { - log.Error(err) - } else { - closeMsg := &schema.CloseQuestionMeta{} - if err := json.Unmarshal([]byte(metaInfo.Value), closeMsg); err != nil { - item.Comment = closeMsg.CloseMsg - } - } - } + item.Comment = as.getTimelineActivityComment(ctx, item.ObjectID, item.ObjectType, item.ActivityType, item.RevisionID) resp.Timeline = append(resp.Timeline, item) } + as.formatTimelineUserInfo(ctx, resp.Timeline) return } +func (as *ActivityService) getTimelineMainObjInfo(ctx context.Context, objectID string) ( + resp *schema.ActObjectInfo, err error) { + resp = &schema.ActObjectInfo{} + objInfo, err := as.objectInfoService.GetInfo(ctx, objectID) + if err != nil { + return nil, err + } + resp.Title = objInfo.Title + if objInfo.ObjectType == constant.TagObjectType { + tag, exist, _ := as.tagCommonService.GetTagByID(ctx, objInfo.TagID) + if exist { + resp.Title = tag.SlugName + resp.MainTagSlugName = tag.MainTagSlugName + } + } + resp.ObjectType = objInfo.ObjectType + resp.QuestionID = objInfo.QuestionID + resp.AnswerID = objInfo.AnswerID + return resp, nil +} + +func (as *ActivityService) getTimelineActivityComment(ctx context.Context, objectID, objectType, + activityType, revisionID string) (comment string) { + if objectType == constant.CommentObjectType { + commentInfo, err := as.commentCommonService.GetComment(ctx, objectID) + if err != nil { + log.Error(err) + } else { + return commentInfo.ParsedText + } + return + } + + if activityType == constant.ActEdited { + revision, err := as.revisionService.GetRevision(ctx, revisionID) + if err != nil { + log.Error(err) + } else { + return revision.Log + } + return + } + if activityType == constant.ActClosed { + // only question can be closed + metaInfo, err := as.metaService.GetMetaByObjectIdAndKey(ctx, objectID, entity.QuestionCloseReasonKey) + if err != nil { + log.Error(err) + } else { + closeMsg := &schema.CloseQuestionMeta{} + if err := json.Unmarshal([]byte(metaInfo.Value), closeMsg); err != nil { + return closeMsg.CloseMsg + } + } + } + return "" +} + +func (as *ActivityService) formatTimelineUserInfo(ctx context.Context, timeline []*schema.ActObjectTimeline) { + userExist := make(map[string]bool) + userIDs := make([]string, 0) + for _, info := range timeline { + if len(info.UserID) == 0 || userExist[info.UserID] { + continue + } + userIDs = append(userIDs, info.UserID) + } + if len(userIDs) == 0 { + return + } + userInfoMapping, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIDs) + if err != nil { + log.Error(err) + return + } + for _, info := range timeline { + if len(info.UserID) == 0 { + continue + } + if userInfo, ok := userInfoMapping[info.UserID]; ok { + info.Username = userInfo.Username + info.UserDisplayName = userInfo.DisplayName + } + } +} + // GetObjectTimelineDetail get object timeline func (as *ActivityService) GetObjectTimelineDetail(ctx context.Context, req *schema.GetObjectTimelineDetailReq) ( resp *schema.GetObjectTimelineDetailResp, err error) { From ba5243040e73035d9b667e69cb3cdf51b7bba287 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Thu, 24 Nov 2022 09:58:58 +0800 Subject: [PATCH 07/14] feat: upgrade add migration config --- internal/migrations/v3.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/internal/migrations/v3.go b/internal/migrations/v3.go index d7e68f10..d61de936 100644 --- a/internal/migrations/v3.go +++ b/internal/migrations/v3.go @@ -3,10 +3,38 @@ package migrations import ( "time" + "github.com/answerdev/answer/internal/entity" "xorm.io/xorm" ) func addActivityTimeline(x *xorm.Engine) error { + defaultConfigTable := []*entity.Config{ + {ID: 87, Key: "question.asked", Value: `0`}, + {ID: 88, Key: "question.closed", Value: `0`}, + {ID: 89, Key: "question.reopened", Value: `0`}, + {ID: 90, Key: "question.answered", Value: `0`}, + {ID: 91, Key: "question.commented", Value: `0`}, + {ID: 92, Key: "question.accept", Value: `0`}, + {ID: 93, Key: "question.edited", Value: `0`}, + {ID: 94, Key: "question.rollback", Value: `0`}, + {ID: 95, Key: "question.deleted", Value: `0`}, + {ID: 96, Key: "question.undeleted", Value: `0`}, + {ID: 97, Key: "answer.answered", Value: `0`}, + {ID: 98, Key: "answer.commented", Value: `0`}, + {ID: 99, Key: "answer.edited", Value: `0`}, + {ID: 100, Key: "answer.rollback", Value: `0`}, + {ID: 101, Key: "answer.undeleted", Value: `0`}, + {ID: 102, Key: "tag.created", Value: `0`}, + {ID: 103, Key: "tag.edited", Value: `0`}, + {ID: 104, Key: "tag.rollback", Value: `0`}, + {ID: 105, Key: "tag.deleted", Value: `0`}, + {ID: 106, Key: "tag.undeleted", Value: `0`}, + } + _, err := x.Insert(defaultConfigTable) + if err != nil { + return err + } + type Revision struct { ReviewUserID int64 `xorm:"not null default 0 BIGINT(20) review_user_id"` } From 4e1da6acec2bd879d5ab848385091cf4b59bd9df Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Thu, 24 Nov 2022 10:12:07 +0800 Subject: [PATCH 08/14] feat: upgrade update migration config --- internal/migrations/v3.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/migrations/v3.go b/internal/migrations/v3.go index d61de936..47dce2f2 100644 --- a/internal/migrations/v3.go +++ b/internal/migrations/v3.go @@ -30,9 +30,17 @@ func addActivityTimeline(x *xorm.Engine) error { {ID: 105, Key: "tag.deleted", Value: `0`}, {ID: 106, Key: "tag.undeleted", Value: `0`}, } - _, err := x.Insert(defaultConfigTable) - if err != nil { - return err + for _, c := range defaultConfigTable { + exist, err := x.Get(&entity.Config{ID: c.ID, Key: c.Key}) + if err != nil { + return err + } + if exist { + continue + } + if _, err := x.Insert(&entity.Config{ID: c.ID, Key: c.Key, Value: c.Value}); err != nil { + return err + } } type Revision struct { From 4e8283eac9e03adc53c4fc4acdb50f9df88849c6 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Thu, 24 Nov 2022 10:48:30 +0800 Subject: [PATCH 09/14] fix: timeline accept answer activity type format, parse the close problem correctly --- internal/base/constant/acticity.go | 3 +++ internal/service/activity/activity.go | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/base/constant/acticity.go b/internal/base/constant/acticity.go index d9b9787f..8300147b 100644 --- a/internal/base/constant/acticity.go +++ b/internal/base/constant/acticity.go @@ -11,6 +11,9 @@ const ( ActVoteUp = "vote_up" ActUpVote = "upvote" ActDownVote = "downvote" + ActFollow = "follow" + ActAccepted = "accepted" + ActAccept = "accept" ) const ( diff --git a/internal/service/activity/activity.go b/internal/service/activity/activity.go index ed399b1c..6169a2b6 100644 --- a/internal/service/activity/activity.go +++ b/internal/service/activity/activity.go @@ -165,7 +165,7 @@ func (as *ActivityService) getTimelineActivityComment(ctx context.Context, objec log.Error(err) } else { closeMsg := &schema.CloseQuestionMeta{} - if err := json.Unmarshal([]byte(metaInfo.Value), closeMsg); err != nil { + if err := json.Unmarshal([]byte(metaInfo.Value), closeMsg); err == nil { return closeMsg.CloseMsg } } @@ -274,7 +274,8 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st func formatActivity(activityType string) (isHidden bool, formattedActivityType string) { if activityType == constant.ActVotedUp || - activityType == constant.ActVotedDown { + activityType == constant.ActVotedDown || + activityType == constant.ActFollow { return true, "" } if activityType == constant.ActVoteUp { @@ -283,5 +284,8 @@ func formatActivity(activityType string) (isHidden bool, formattedActivityType s if activityType == constant.ActVoteDown { return false, constant.ActDownVote } + if activityType == constant.ActAccepted { + return false, constant.ActAccept + } return false, activityType } From bc2012a35924a1a342cf0b565064c6fc2cd19077 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Thu, 24 Nov 2022 10:56:55 +0800 Subject: [PATCH 10/14] fix: timeline main object info that label information exists --- internal/repo/repo_test/tag_repo_test.go | 8 ++++---- internal/repo/tag_common/tag_common_repo.go | 6 ++++-- internal/service/object_info/object_info.go | 2 +- internal/service/tag_common/tag_common.go | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/internal/repo/repo_test/tag_repo_test.go b/internal/repo/repo_test/tag_repo_test.go index d659651c..d7b5e71c 100644 --- a/internal/repo/repo_test/tag_repo_test.go +++ b/internal/repo/repo_test/tag_repo_test.go @@ -54,7 +54,7 @@ func Test_tagRepo_GetTagByID(t *testing.T) { tagOnce.Do(addTagList) tagCommonRepo := tag_common.NewTagCommonRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource)) - gotTag, exist, err := tagCommonRepo.GetTagByID(context.TODO(), testTagList[0].ID) + gotTag, exist, err := tagCommonRepo.GetTagByID(context.TODO(), testTagList[0].ID, true) assert.NoError(t, err) assert.True(t, exist) assert.Equal(t, testTagList[0].SlugName, gotTag.SlugName) @@ -139,7 +139,7 @@ func Test_tagRepo_UpdateTag(t *testing.T) { tagCommonRepo := tag_common.NewTagCommonRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource)) - gotTag, exist, err := tagCommonRepo.GetTagByID(context.TODO(), testTagList[0].ID) + gotTag, exist, err := tagCommonRepo.GetTagByID(context.TODO(), testTagList[0].ID, true) assert.NoError(t, err) assert.True(t, exist) assert.Equal(t, testTagList[0].DisplayName, gotTag.DisplayName) @@ -152,7 +152,7 @@ func Test_tagRepo_UpdateTagQuestionCount(t *testing.T) { err := tagCommonRepo.UpdateTagQuestionCount(context.TODO(), testTagList[0].ID, 100) assert.NoError(t, err) - gotTag, exist, err := tagCommonRepo.GetTagByID(context.TODO(), testTagList[0].ID) + gotTag, exist, err := tagCommonRepo.GetTagByID(context.TODO(), testTagList[0].ID, true) assert.NoError(t, err) assert.True(t, exist) assert.Equal(t, 100, gotTag.QuestionCount) @@ -172,7 +172,7 @@ func Test_tagRepo_UpdateTagSynonym(t *testing.T) { tagCommonRepo := tag_common.NewTagCommonRepo(testDataSource, unique.NewUniqueIDRepo(testDataSource)) - gotTag, exist, err := tagCommonRepo.GetTagByID(context.TODO(), testTagList[2].ID) + gotTag, exist, err := tagCommonRepo.GetTagByID(context.TODO(), testTagList[2].ID, true) assert.NoError(t, err) assert.True(t, exist) assert.Equal(t, testTagList[0].ID, fmt.Sprintf("%d", gotTag.MainTagID)) diff --git a/internal/repo/tag_common/tag_common_repo.go b/internal/repo/tag_common/tag_common_repo.go index 50f8517f..99f16cc4 100644 --- a/internal/repo/tag_common/tag_common_repo.go +++ b/internal/repo/tag_common/tag_common_repo.go @@ -122,12 +122,14 @@ func (tr *tagCommonRepo) GetTagListByNames(ctx context.Context, names []string) } // GetTagByID get tag one -func (tr *tagCommonRepo) GetTagByID(ctx context.Context, tagID string) ( +func (tr *tagCommonRepo) GetTagByID(ctx context.Context, tagID string, includeDeleted bool) ( tag *entity.Tag, exist bool, err error, ) { tag = &entity.Tag{} session := tr.data.DB.Where(builder.Eq{"id": tagID}) - session.Where(builder.Eq{"status": entity.TagStatusAvailable}) + if !includeDeleted { + session.Where(builder.Eq{"status": entity.TagStatusAvailable}) + } exist, err = session.Get(tag) if err != nil { return nil, false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() diff --git a/internal/service/object_info/object_info.go b/internal/service/object_info/object_info.go index aea2208d..2aa2f8ae 100644 --- a/internal/service/object_info/object_info.go +++ b/internal/service/object_info/object_info.go @@ -113,7 +113,7 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc } } case constant.TagObjectType: - tagInfo, exist, err := os.tagRepo.GetTagByID(ctx, objectID) + tagInfo, exist, err := os.tagRepo.GetTagByID(ctx, objectID, true) if err != nil { return nil, err } diff --git a/internal/service/tag_common/tag_common.go b/internal/service/tag_common/tag_common.go index ef04c06b..16b297a5 100644 --- a/internal/service/tag_common/tag_common.go +++ b/internal/service/tag_common/tag_common.go @@ -25,7 +25,7 @@ type TagCommonRepo interface { GetTagBySlugName(ctx context.Context, slugName string) (tagInfo *entity.Tag, exist bool, err error) GetTagListByName(ctx context.Context, name string, limit int, hasReserved bool) (tagList []*entity.Tag, err error) GetTagListByNames(ctx context.Context, names []string) (tagList []*entity.Tag, err error) - GetTagByID(ctx context.Context, tagID string) (tag *entity.Tag, exist bool, err error) + GetTagByID(ctx context.Context, tagID string, includeDeleted bool) (tag *entity.Tag, exist bool, err error) GetTagPage(ctx context.Context, page, pageSize int, tag *entity.Tag, queryCond string) (tagList []*entity.Tag, total int64, err error) GetRecommendTagList(ctx context.Context) (tagList []*entity.Tag, err error) GetReservedTagList(ctx context.Context) (tagList []*entity.Tag, err error) @@ -209,7 +209,7 @@ func (ts *TagCommonService) AddTagList(ctx context.Context, tagList []*entity.Ta // GetTagByID get object tag func (ts *TagCommonService) GetTagByID(ctx context.Context, tagID string) (tag *entity.Tag, exist bool, err error) { - tag, exist, err = ts.tagCommonRepo.GetTagByID(ctx, tagID) + tag, exist, err = ts.tagCommonRepo.GetTagByID(ctx, tagID, false) if !exist { return } From 06d358438dad0507e6e08d4dffe9820217dd3919 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Fri, 25 Nov 2022 10:27:13 +0800 Subject: [PATCH 11/14] feat: version upgrade add rank limiting condition --- cmd/answer/main.go | 4 +-- internal/entity/config_entity.go | 2 +- internal/migrations/v3.go | 46 +++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/cmd/answer/main.go b/cmd/answer/main.go index 8d1481ba..cea99db9 100644 --- a/cmd/answer/main.go +++ b/cmd/answer/main.go @@ -35,12 +35,12 @@ var ( // @in header // @name Authorization func main() { + log.SetLogger(zap.NewLogger( + log.ParseLevel(logLevel), zap.WithName("answer"), zap.WithPath(logPath), zap.WithCallerFullPath())) Execute() } func runApp() { - log.SetLogger(zap.NewLogger( - log.ParseLevel(logLevel), zap.WithName("answer"), zap.WithPath(logPath), zap.WithCallerFullPath())) c, err := conf.ReadConfig(cli.GetConfigFilePath()) if err != nil { panic(err) diff --git a/internal/entity/config_entity.go b/internal/entity/config_entity.go index 1d716a2d..361e7acc 100644 --- a/internal/entity/config_entity.go +++ b/internal/entity/config_entity.go @@ -3,7 +3,7 @@ package entity // Config config type Config struct { ID int `xorm:"not null pk autoincr INT(11) id"` - Key string `xorm:"unique VARCHAR(32) key"` + Key string `xorm:"unique VARCHAR(128) key"` Value string `xorm:"TEXT value"` } diff --git a/internal/migrations/v3.go b/internal/migrations/v3.go index 47dce2f2..2a252d0c 100644 --- a/internal/migrations/v3.go +++ b/internal/migrations/v3.go @@ -4,11 +4,41 @@ import ( "time" "github.com/answerdev/answer/internal/entity" + "github.com/segmentfault/pacman/log" "xorm.io/xorm" ) func addActivityTimeline(x *xorm.Engine) error { + // only increasing field length to 128 + type Config struct { + Key string `xorm:"unique VARCHAR(128) key"` + } + if err := x.Sync(new(Config)); err != nil { + return err + } defaultConfigTable := []*entity.Config{ + {ID: 36, Key: "rank.question.add", Value: `1`}, + {ID: 37, Key: "rank.question.edit", Value: `200`}, + {ID: 38, Key: "rank.question.delete", Value: `0`}, + {ID: 39, Key: "rank.question.vote_up", Value: `15`}, + {ID: 40, Key: "rank.question.vote_down", Value: `125`}, + {ID: 41, Key: "rank.answer.add", Value: `1`}, + {ID: 42, Key: "rank.answer.edit", Value: `200`}, + {ID: 43, Key: "rank.answer.delete", Value: `0`}, + {ID: 44, Key: "rank.answer.accept", Value: `1`}, + {ID: 45, Key: "rank.answer.vote_up", Value: `15`}, + {ID: 46, Key: "rank.answer.vote_down", Value: `125`}, + {ID: 47, Key: "rank.comment.add", Value: `1`}, + {ID: 48, Key: "rank.comment.edit", Value: `1`}, + {ID: 49, Key: "rank.comment.delete", Value: `0`}, + {ID: 50, Key: "rank.report.add", Value: `1`}, + {ID: 51, Key: "rank.tag.add", Value: `1`}, + {ID: 52, Key: "rank.tag.edit", Value: `100`}, + {ID: 53, Key: "rank.tag.delete", Value: `0`}, + {ID: 54, Key: "rank.tag.synonym", Value: `20000`}, + {ID: 55, Key: "rank.link.url_limit", Value: `10`}, + {ID: 56, Key: "rank.vote.detail", Value: `0`}, + {ID: 87, Key: "question.asked", Value: `0`}, {ID: 88, Key: "question.closed", Value: `0`}, {ID: 89, Key: "question.reopened", Value: `0`}, @@ -29,6 +59,15 @@ func addActivityTimeline(x *xorm.Engine) error { {ID: 104, Key: "tag.rollback", Value: `0`}, {ID: 105, Key: "tag.deleted", Value: `0`}, {ID: 106, Key: "tag.undeleted", Value: `0`}, + + {ID: 107, Key: "rank.comment.vote_up", Value: `1`}, + {ID: 108, Key: "rank.comment.vote_down", Value: `1`}, + {ID: 109, Key: "rank.question.edit_without_review", Value: `2000`}, + {ID: 110, Key: "rank.answer.edit_without_review", Value: `2000`}, + {ID: 111, Key: "rank.tag.edit_without_review", Value: `20000`}, + {ID: 112, Key: "rank.answer.audit", Value: `2000`}, + {ID: 113, Key: "rank.question.audit", Value: `2000`}, + {ID: 114, Key: "rank.tag.audit", Value: `20000`}, } for _, c := range defaultConfigTable { exist, err := x.Get(&entity.Config{ID: c.ID, Key: c.Key}) @@ -36,9 +75,14 @@ func addActivityTimeline(x *xorm.Engine) error { return err } if exist { + if _, err = x.Update(c, &entity.Config{ID: c.ID, Key: c.Key}); err != nil { + log.Errorf("update %+v config failed: %s", c, err) + return err + } continue } - if _, err := x.Insert(&entity.Config{ID: c.ID, Key: c.Key, Value: c.Value}); err != nil { + if _, err = x.Insert(&entity.Config{ID: c.ID, Key: c.Key, Value: c.Value}); err != nil { + log.Errorf("insert %+v config failed: %s", c, err) return err } } From e54ebacc22d2ded3f88df8c5cda660f5546ec560 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Fri, 25 Nov 2022 12:20:50 +0800 Subject: [PATCH 12/14] feat: set question answer comment delete rank -1 --- internal/migrations/v3.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/migrations/v3.go b/internal/migrations/v3.go index 2a252d0c..651b6dd0 100644 --- a/internal/migrations/v3.go +++ b/internal/migrations/v3.go @@ -19,22 +19,22 @@ func addActivityTimeline(x *xorm.Engine) error { defaultConfigTable := []*entity.Config{ {ID: 36, Key: "rank.question.add", Value: `1`}, {ID: 37, Key: "rank.question.edit", Value: `200`}, - {ID: 38, Key: "rank.question.delete", Value: `0`}, + {ID: 38, Key: "rank.question.delete", Value: `-1`}, {ID: 39, Key: "rank.question.vote_up", Value: `15`}, {ID: 40, Key: "rank.question.vote_down", Value: `125`}, {ID: 41, Key: "rank.answer.add", Value: `1`}, {ID: 42, Key: "rank.answer.edit", Value: `200`}, - {ID: 43, Key: "rank.answer.delete", Value: `0`}, + {ID: 43, Key: "rank.answer.delete", Value: `-1`}, {ID: 44, Key: "rank.answer.accept", Value: `1`}, {ID: 45, Key: "rank.answer.vote_up", Value: `15`}, {ID: 46, Key: "rank.answer.vote_down", Value: `125`}, {ID: 47, Key: "rank.comment.add", Value: `1`}, {ID: 48, Key: "rank.comment.edit", Value: `1`}, - {ID: 49, Key: "rank.comment.delete", Value: `0`}, + {ID: 49, Key: "rank.comment.delete", Value: `-1`}, {ID: 50, Key: "rank.report.add", Value: `1`}, {ID: 51, Key: "rank.tag.add", Value: `1`}, {ID: 52, Key: "rank.tag.edit", Value: `100`}, - {ID: 53, Key: "rank.tag.delete", Value: `0`}, + {ID: 53, Key: "rank.tag.delete", Value: `-1`}, {ID: 54, Key: "rank.tag.synonym", Value: `20000`}, {ID: 55, Key: "rank.link.url_limit", Value: `10`}, {ID: 56, Key: "rank.vote.detail", Value: `0`}, From 381c61d3120c115cb79ad248e95e792572c3b2f4 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Fri, 25 Nov 2022 15:35:12 +0800 Subject: [PATCH 13/14] feat: tag add user id --- internal/migrations/v3.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/migrations/v3.go b/internal/migrations/v3.go index 651b6dd0..0f955e8d 100644 --- a/internal/migrations/v3.go +++ b/internal/migrations/v3.go @@ -95,5 +95,8 @@ func addActivityTimeline(x *xorm.Engine) error { RevisionID int64 `xorm:"not null default 0 BIGINT(20) revision_id"` OriginalObjectID string `xorm:"not null default 0 BIGINT(20) original_object_id"` } - return x.Sync(new(Activity), new(Revision)) + type Tag struct { + UserID string `xorm:"not null default 0 BIGINT(20) user_id"` + } + return x.Sync(new(Activity), new(Revision), new(Tag)) } From 6e8cfb4ac54092d941fb033fd124f0df195fc67e Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Fri, 25 Nov 2022 18:00:50 +0800 Subject: [PATCH 14/14] fix: update the initial reputation value --- internal/migrations/init.go | 48 +++++++++++++++++++++---------------- internal/migrations/v3.go | 2 +- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/internal/migrations/init.go b/internal/migrations/init.go index 9c4bb522..d3a518d7 100644 --- a/internal/migrations/init.go +++ b/internal/migrations/init.go @@ -191,26 +191,26 @@ func initConfigTable(engine *xorm.Engine) error { {ID: 32, Key: "question.follow", Value: `0`}, {ID: 33, Key: "email.config", Value: `{"from_name":"","from_email":"","smtp_host":"","smtp_port":465,"smtp_password":"","smtp_username":"","smtp_authentication":true,"encryption":"","register_title":"[{{.SiteName}}] Confirm your new account","register_body":"Welcome to {{.SiteName}}

\n\nClick the following link to confirm and activate your new account:
\n{{.RegisterUrl}}

\n\nIf the above link is not clickable, try copying and pasting it into the address bar of your web browser.\n","pass_reset_title":"[{{.SiteName }}] Password reset","pass_reset_body":"Somebody asked to reset your password on [{{.SiteName}}].

\n\nIf it was not you, you can safely ignore this email.

\n\nClick the following link to choose a new password:
\n{{.PassResetUrl}}\n","change_title":"[{{.SiteName}}] Confirm your new email address","change_body":"Confirm your new email address for {{.SiteName}} by clicking on the following link:

\n\n{{.ChangeEmailUrl}}

\n\nIf you did not request this change, please ignore this email.\n","test_title":"[{{.SiteName}}] Test Email","test_body":"This is a test email."}`}, {ID: 35, Key: "tag.follow", Value: `0`}, - {ID: 36, Key: "rank.question.add", Value: `0`}, - {ID: 37, Key: "rank.question.edit", Value: `0`}, - {ID: 38, Key: "rank.question.delete", Value: `0`}, - {ID: 39, Key: "rank.question.vote_up", Value: `0`}, - {ID: 40, Key: "rank.question.vote_down", Value: `0`}, - {ID: 41, Key: "rank.answer.add", Value: `0`}, - {ID: 42, Key: "rank.answer.edit", Value: `0`}, - {ID: 43, Key: "rank.answer.delete", Value: `0`}, - {ID: 44, Key: "rank.answer.accept", Value: `0`}, - {ID: 45, Key: "rank.answer.vote_up", Value: `0`}, - {ID: 46, Key: "rank.answer.vote_down", Value: `0`}, - {ID: 47, Key: "rank.comment.add", Value: `0`}, - {ID: 48, Key: "rank.comment.edit", Value: `0`}, - {ID: 49, Key: "rank.comment.delete", Value: `0`}, - {ID: 50, Key: "rank.report.add", Value: `0`}, - {ID: 51, Key: "rank.tag.add", Value: `0`}, - {ID: 52, Key: "rank.tag.edit", Value: `0`}, - {ID: 53, Key: "rank.tag.delete", Value: `0`}, - {ID: 54, Key: "rank.tag.synonym", Value: `0`}, - {ID: 55, Key: "rank.link.url_limit", Value: `0`}, + {ID: 36, Key: "rank.question.add", Value: `1`}, + {ID: 37, Key: "rank.question.edit", Value: `200`}, + {ID: 38, Key: "rank.question.delete", Value: `-1`}, + {ID: 39, Key: "rank.question.vote_up", Value: `15`}, + {ID: 40, Key: "rank.question.vote_down", Value: `125`}, + {ID: 41, Key: "rank.answer.add", Value: `1`}, + {ID: 42, Key: "rank.answer.edit", Value: `200`}, + {ID: 43, Key: "rank.answer.delete", Value: `-1`}, + {ID: 44, Key: "rank.answer.accept", Value: `1`}, + {ID: 45, Key: "rank.answer.vote_up", Value: `15`}, + {ID: 46, Key: "rank.answer.vote_down", Value: `125`}, + {ID: 47, Key: "rank.comment.add", Value: `1`}, + {ID: 48, Key: "rank.comment.edit", Value: `-1`}, + {ID: 49, Key: "rank.comment.delete", Value: `-1`}, + {ID: 50, Key: "rank.report.add", Value: `1`}, + {ID: 51, Key: "rank.tag.add", Value: `1`}, + {ID: 52, Key: "rank.tag.edit", Value: `100`}, + {ID: 53, Key: "rank.tag.delete", Value: `-1`}, + {ID: 54, Key: "rank.tag.synonym", Value: `20000`}, + {ID: 55, Key: "rank.link.url_limit", Value: `10`}, {ID: 56, Key: "rank.vote.detail", Value: `0`}, {ID: 57, Key: "reason.spam", Value: `{"name":"spam","description":"This post is an advertisement, or vandalism. It is not useful or relevant to the current topic."}`}, {ID: 58, Key: "reason.rude_or_abusive", Value: `{"name":"rude or abusive","description":"A reasonable person would find this content inappropriate for respectful discourse."}`}, @@ -262,6 +262,14 @@ func initConfigTable(engine *xorm.Engine) error { {ID: 104, Key: "tag.rollback", Value: `0`}, {ID: 105, Key: "tag.deleted", Value: `0`}, {ID: 106, Key: "tag.undeleted", Value: `0`}, + {ID: 107, Key: "rank.comment.vote_up", Value: `1`}, + {ID: 108, Key: "rank.comment.vote_down", Value: `1`}, + {ID: 109, Key: "rank.question.edit_without_review", Value: `2000`}, + {ID: 110, Key: "rank.answer.edit_without_review", Value: `2000`}, + {ID: 111, Key: "rank.tag.edit_without_review", Value: `20000`}, + {ID: 112, Key: "rank.answer.audit", Value: `2000`}, + {ID: 113, Key: "rank.question.audit", Value: `2000`}, + {ID: 114, Key: "rank.tag.audit", Value: `20000`}, } _, err := engine.Insert(defaultConfigTable) return err diff --git a/internal/migrations/v3.go b/internal/migrations/v3.go index 0f955e8d..11d613ef 100644 --- a/internal/migrations/v3.go +++ b/internal/migrations/v3.go @@ -29,7 +29,7 @@ func addActivityTimeline(x *xorm.Engine) error { {ID: 45, Key: "rank.answer.vote_up", Value: `15`}, {ID: 46, Key: "rank.answer.vote_down", Value: `125`}, {ID: 47, Key: "rank.comment.add", Value: `1`}, - {ID: 48, Key: "rank.comment.edit", Value: `1`}, + {ID: 48, Key: "rank.comment.edit", Value: `-1`}, {ID: 49, Key: "rank.comment.delete", Value: `-1`}, {ID: 50, Key: "rank.report.add", Value: `1`}, {ID: 51, Key: "rank.tag.add", Value: `1`},