diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 121be734..d173ee2c 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -204,7 +204,13 @@ backend: other: "something else" desc: other: "This post requires another reason not listed above." - + operation_type: + asked: + other: "asked" + answered: + other: "answered" + modified: + other: "modified" notification: action: update_question: diff --git a/internal/controller/question_controller.go b/internal/controller/question_controller.go index 94c7b92c..dd0ee9ac 100644 --- a/internal/controller/question_controller.go +++ b/internal/controller/question_controller.go @@ -1,10 +1,9 @@ package controller import ( - "context" - "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/middleware" + "github.com/answerdev/answer/internal/base/pager" "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/validator" "github.com/answerdev/answer/internal/entity" @@ -31,7 +30,7 @@ func NewQuestionController(questionService *service.QuestionService, rankService // RemoveQuestion delete question // @Summary delete question // @Description delete question -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth @@ -62,7 +61,7 @@ func (qc *QuestionController) RemoveQuestion(ctx *gin.Context) { // CloseQuestion Close question // @Summary Close question // @Description Close question -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth @@ -92,7 +91,7 @@ func (qc *QuestionController) CloseQuestion(ctx *gin.Context) { // ReopenQuestion reopen question // @Summary reopen question // @Description reopen question -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth @@ -119,10 +118,10 @@ func (qc *QuestionController) ReopenQuestion(ctx *gin.Context) { handler.HandleResponse(ctx, err, nil) } -// GetQuestion godoc -// @Summary GetQuestion Question -// @Description GetQuestion Question -// @Tags api-question +// GetQuestion get question details +// @Summary get question details +// @Description get question details +// @Tags Question // @Security ApiKeyAuth // @Accept json // @Produce json @@ -161,7 +160,7 @@ func (qc *QuestionController) GetQuestion(ctx *gin.Context) { // SimilarQuestion godoc // @Summary Search Similar Question // @Description Search Similar Question -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Param question_id query string true "question_id" default() @@ -181,65 +180,34 @@ func (qc *QuestionController) SimilarQuestion(ctx *gin.Context) { }) } -// Index godoc -// @Summary SearchQuestionList -// @Description SearchQuestionList
"order" Enums(newest, active,frequent,score,unanswered) -// @Tags api-question +// QuestionPage get questions by page +// @Summary get questions by page +// @Description get questions by page +// @Tags Question // @Accept json // @Produce json -// @Param data body schema.QuestionSearch true "QuestionSearch" -// @Success 200 {string} string "" +// @Param data body schema.QuestionPageReq true "QuestionPageReq" +// @Success 200 {object} handler.RespBody{data=pager.PageModel{list=[]schema.QuestionPageResp}} // @Router /answer/api/v1/question/page [get] -func (qc *QuestionController) Index(ctx *gin.Context) { - req := &schema.QuestionSearch{} +func (qc *QuestionController) QuestionPage(ctx *gin.Context) { + req := &schema.QuestionPageReq{} if handler.BindAndCheck(ctx, req) { return } - userID := middleware.GetLoginUserIDFromContext(ctx) - list, count, err := qc.questionService.SearchList(ctx, req, userID) + req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx) + + questions, total, err := qc.questionService.GetQuestionPage(ctx, req) if err != nil { handler.HandleResponse(ctx, err, nil) return } - handler.HandleResponse(ctx, nil, gin.H{ - "list": list, - "count": count, - }) -} - -// SearchList godoc -// @Summary SearchQuestionList -// @Description SearchQuestionList -// @Tags api-question -// @Accept json -// @Produce json -// @Param data body schema.QuestionSearch true "QuestionSearch" -// @Router /answer/api/v1/question/search [post] -// @Success 200 {string} string "" -func (qc *QuestionController) SearchList(c *gin.Context) { - Request := new(schema.QuestionSearch) - err := c.BindJSON(Request) - if err != nil { - handler.HandleResponse(c, err, nil) - return - } - ctx := context.Background() - userID := middleware.GetLoginUserIDFromContext(c) - list, count, err := qc.questionService.SearchList(ctx, Request, userID) - if err != nil { - handler.HandleResponse(c, err, nil) - return - } - handler.HandleResponse(c, nil, gin.H{ - "list": list, - "count": count, - }) + handler.HandleResponse(ctx, nil, pager.NewPageModel(total, questions)) } // AddQuestion add question // @Summary add question // @Description add question -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth @@ -309,7 +277,7 @@ func (qc *QuestionController) AddQuestion(ctx *gin.Context) { // UpdateQuestion update question // @Summary update question // @Description update question -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth @@ -366,7 +334,7 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) { // CloseMsgList close question msg list // @Summary close question msg list // @Description close question msg list -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth @@ -380,7 +348,7 @@ func (qc *QuestionController) CloseMsgList(ctx *gin.Context) { // SearchByTitleLike add question title like // @Summary add question title like // @Description add question title like -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth @@ -397,7 +365,7 @@ func (qc *QuestionController) SearchByTitleLike(ctx *gin.Context) { // UserTop godoc // @Summary UserTop // @Description UserTop -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth @@ -417,7 +385,7 @@ func (qc *QuestionController) UserTop(ctx *gin.Context) { // UserList godoc // @Summary UserList // @Description UserList -// @Tags api-question +// @Tags Question // @Accept json // @Produce json // @Security ApiKeyAuth diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 6d4327cc..c97c81b4 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -91,8 +91,8 @@ func (tc *TemplateController) SiteInfo(ctx *gin.Context) *schema.TemplateSiteInf // Index question list func (tc *TemplateController) Index(ctx *gin.Context) { - req := &schema.QuestionSearch{ - Order: "newest", + req := &schema.QuestionPageReq{ + OrderCond: "newest", } if handler.BindAndCheck(ctx, req) { tc.Page404(ctx) @@ -124,8 +124,8 @@ func (tc *TemplateController) Index(ctx *gin.Context) { } func (tc *TemplateController) QuestionList(ctx *gin.Context) { - req := &schema.QuestionSearch{ - Order: "newest", + req := &schema.QuestionPageReq{ + OrderCond: "newest", } if handler.BindAndCheck(ctx, req) { tc.Page404(ctx) diff --git a/internal/controller/template_render/question.go b/internal/controller/template_render/question.go index 5025979c..fc0e5bab 100644 --- a/internal/controller/template_render/question.go +++ b/internal/controller/template_render/question.go @@ -11,8 +11,8 @@ import ( "github.com/segmentfault/pacman/log" ) -func (t *TemplateRenderController) Index(ctx *gin.Context, req *schema.QuestionSearch) ([]*schema.QuestionInfo, int64, error) { - return t.questionService.SearchList(ctx, req, req.UserID) +func (t *TemplateRenderController) Index(ctx *gin.Context, req *schema.QuestionPageReq) ([]*schema.QuestionPageResp, int64, error) { + return t.questionService.GetQuestionPage(ctx, req) } func (t *TemplateRenderController) QuestionDetail(ctx *gin.Context, id string) (resp *schema.QuestionInfo, err error) { diff --git a/internal/controller/template_render/tags.go b/internal/controller/template_render/tags.go index d8a36b64..7e9268f2 100644 --- a/internal/controller/template_render/tags.go +++ b/internal/controller/template_render/tags.go @@ -15,19 +15,20 @@ func (q *TemplateRenderController) TagList(ctx context.Context, req *schema.GetT return } -func (q *TemplateRenderController) TagInfo(ctx context.Context, req *schema.GetTamplateTagInfoReq) (resp *schema.GetTagResp, questionList []*schema.QuestionInfo, questionCount int64, err error) { +func (q *TemplateRenderController) TagInfo(ctx context.Context, req *schema.GetTamplateTagInfoReq) (resp *schema.GetTagResp, questionList []*schema.QuestionPageResp, questionCount int64, err error) { dto := &schema.GetTagInfoReq{} _ = copier.Copy(dto, req) resp, err = q.tagService.GetTagInfo(ctx, dto) if err != nil { return } - searchQuestion := &schema.QuestionSearch{} + searchQuestion := &schema.QuestionPageReq{} searchQuestion.Page = req.Page searchQuestion.PageSize = req.PageSize - searchQuestion.Order = "newest" + searchQuestion.OrderCond = "newest" searchQuestion.Tag = req.Name - questionList, questionCount, err = q.questionService.SearchList(ctx, searchQuestion, "") + searchQuestion.LoginUserID = req.UserID + questionList, questionCount, err = q.questionService.GetQuestionPage(ctx, searchQuestion) if err != nil { return } diff --git a/internal/entity/question_entity.go b/internal/entity/question_entity.go index a47f59b4..4b4f4923 100644 --- a/internal/entity/question_entity.go +++ b/internal/entity/question_entity.go @@ -22,11 +22,6 @@ var AdminQuestionSearchStatusIntToString = map[int]string{ QuestionStatusDeleted: "deleted", } -type QuestionTag struct { - Question `xorm:"extends"` - TagRel `xorm:"extends"` -} - // Question question type Question struct { ID string `xorm:"not null pk BIGINT(20) id"` diff --git a/internal/repo/question/question_repo.go b/internal/repo/question/question_repo.go index 3f44a188..963cf667 100644 --- a/internal/repo/question/question_repo.go +++ b/internal/repo/question/question_repo.go @@ -205,70 +205,40 @@ func (qr *questionRepo) GetQuestionIDsPage(ctx context.Context, page, pageSize i return questionIDList, nil } -// GetQuestionPage get question page -func (qr *questionRepo) GetQuestionPage(ctx context.Context, page, pageSize int, question *entity.Question) (questionList []*entity.Question, total int64, err error) { +// GetQuestionPage query question page +func (qr *questionRepo) GetQuestionPage(ctx context.Context, page, pageSize int, userID, tagID, orderCond string) ( + questionList []*entity.Question, total int64, err error) { questionList = make([]*entity.Question, 0) - total, err = pager.Help(page, pageSize, questionList, question, qr.data.DB.NewSession()) - if err != nil { - err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() - } - return -} -// SearchList -func (qr *questionRepo) SearchList(ctx context.Context, search *schema.QuestionSearch) ([]*entity.QuestionTag, int64, error) { - var count int64 - var err error - rows := make([]*entity.QuestionTag, 0) - if search.Page > 0 { - search.Page = search.Page - 1 - } else { - search.Page = 0 + session := qr.data.DB.Where("question.status = ? OR question.status = ?", + entity.QuestionStatusAvailable, entity.QuestionStatusClosed) + if len(tagID) > 0 { + session.Join("LEFT", "tag_rel", "question.id = tag_rel.object_id") + session.And("tag_rel.tag_id = ?", tagID) + session.And("tag_rel.status = ?", entity.TagRelStatusAvailable) } - if search.PageSize == 0 { - search.PageSize = constant.DefaultPageSize + if len(userID) > 0 { + session.And("question.user_id = ?", userID) } - offset := search.Page * search.PageSize - session := qr.data.DB.Table("question") - - if len(search.TagIDs) > 0 { - session = session.Join("LEFT", "tag_rel", "question.id = tag_rel.object_id") - session = session.And("tag_rel.tag_id =?", search.TagIDs[0]) - // session = session.In("tag_rel.tag_id ", search.TagIDs) - session = session.And("tag_rel.status =?", entity.TagRelStatusAvailable) - } - - if len(search.UserID) > 0 { - session = session.And("question.user_id = ?", search.UserID) - } - - session = session.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusClosed}) - // if search.Status > 0 { - // session = session.And("question.status = ?", search.Status) - // } - // switch - // newest, active,frequent,score,unanswered - switch search.Order { + switch orderCond { case "newest": - session = session.OrderBy("question.created_at desc") + session.OrderBy("question.created_at DESC") case "active": - session = session.OrderBy("question.post_update_time desc,question.updated_at desc") + session.OrderBy("question.post_update_time DESC, question.updated_at DESC") case "frequent": - session = session.OrderBy("question.view_count desc") + session.OrderBy("question.view_count DESC") case "score": - session = session.OrderBy("question.vote_count desc,question.view_count desc") + session.OrderBy("question.vote_count DESC, question.view_count DESC") case "unanswered": - session = session.And("question.last_answer_id = 0") - session = session.OrderBy("question.created_at desc") + session.Where("question.last_answer_id = 0") + session.OrderBy("question.created_at DESC") } - session = session.Limit(search.PageSize, offset) - session = session.Select("question.id,question.user_id,last_edit_user_id,question.title,question.original_text,question.parsed_text,question.status,question.view_count,question.unique_view_count,question.vote_count,question.answer_count,question.collection_count,question.follow_count,question.accepted_answer_id,question.last_answer_id,question.created_at,question.updated_at,question.post_update_time,question.revision_id") - count, err = session.FindAndCount(&rows) + + total, err = pager.Help(page, pageSize, &questionList, &entity.Question{}, session) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() - return rows, count, err } - return rows, count, nil + return questionList, total, err } func (qr *questionRepo) AdminSearchList(ctx context.Context, search *schema.AdminQuestionSearch) ([]*entity.Question, int64, error) { diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index d425c746..8c61a426 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -124,8 +124,7 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) { //question r.GET("/question/info", a.questionController.GetQuestion) - r.POST("/question/search", a.questionController.SearchList) - r.GET("/question/page", a.questionController.Index) + r.GET("/question/page", a.questionController.QuestionPage) r.GET("/question/similar/tag", a.questionController.SimilarQuestion) r.GET("/personal/qa/top", a.questionController.UserTop) r.GET("/personal/question/page", a.questionController.UserList) @@ -143,7 +142,6 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) { r.GET("/tags/following", a.tagController.GetFollowingTags) r.GET("/tag", a.tagController.GetTagInfo) r.GET("/tag/synonyms", a.tagController.GetTagSynonyms) - r.GET("/question/index", a.questionController.Index) //search r.GET("/search", a.searchController.Search) diff --git a/internal/schema/question_schema.go b/internal/schema/question_schema.go index f5689297..6d0f93c5 100644 --- a/internal/schema/question_schema.go +++ b/internal/schema/question_schema.go @@ -1,6 +1,8 @@ package schema import ( + "time" + "github.com/answerdev/answer/internal/base/validator" "github.com/answerdev/answer/pkg/converter" ) @@ -222,15 +224,66 @@ type UserQuestionInfo struct { Status string `json:"status"` } -type QuestionSearch struct { - Page int `json:"page" form:"page"` // Query number of pages - PageSize int `json:"page_size" form:"page_size"` // Search page size - Order string `json:"order" form:"order"` // Search order by - // Tags []string `json:"tags" form:"tags"` // Search tag - Tag string `json:"tag" form:"tag"` //Search tag - TagIDs []string `json:"-" form:"-"` // Search tag - UserName string `json:"username" form:"username"` // Search username - UserID string `json:"-" form:"-"` +const ( + QuestionOrderCondNewest = "newest" + QuestionOrderCondActive = "active" + QuestionOrderCondFrequent = "frequent" + QuestionOrderCondScore = "score" + QuestionOrderCondUnanswered = "unanswered" +) + +// QuestionPageReq query questions page +type QuestionPageReq struct { + Page int `validate:"omitempty,min=1" form:"page"` + PageSize int `validate:"omitempty,min=1" form:"page_size"` + OrderCond string `validate:"omitempty,oneof=newest active frequent score unanswered" form:"order"` + Tag string `validate:"omitempty,gt=0,lte=100" form:"tag"` + Username string `validate:"omitempty,gt=0,lte=100" form:"username"` + + LoginUserID string `json:"-"` + UserIDBeSearched string `json:"-"` + TagID string `json:"-"` +} + +const ( + QuestionPageRespOperationTypeAsked = "question.operation_type.asked" + QuestionPageRespOperationTypeAnswered = "question.operation_type.answered" + QuestionPageRespOperationTypeModified = "question.operation_type.modified" +) + +type QuestionPageResp struct { + ID string `json:"id" ` + Title string `json:"title"` + UrlTitle string `json:"url_title"` + Description string `json:"description"` + Status int `json:"status"` + Tags []*TagResp `json:"tags"` + + // question statistical information + ViewCount int `json:"view_count"` + UniqueViewCount int `json:"unique_view_count"` + VoteCount int `json:"vote_count"` + AnswerCount int `json:"answer_count"` + CollectionCount int `json:"collection_count"` + FollowCount int `json:"follow_count"` + + // answer information + AcceptedAnswerID string `json:"accepted_answer_id"` + LastAnswerID string `json:"last_answer_id"` + LastAnsweredUserID string `json:"-"` + LastAnsweredAt time.Time `json:"-"` + + // operator information + OperatedAt int64 `json:"operated_at"` + Operator *QuestionPageRespOperator `json:"operator"` + OperationType string `json:"operation_type"` +} + +type QuestionPageRespOperator struct { + ID string `json:"id"` + Username string `json:"username"` + Rank int `json:"rank"` + DisplayName string `json:"display_name"` } type AdminQuestionSearch struct { diff --git a/internal/service/notification_common/notification.go b/internal/service/notification_common/notification.go index 5bb23f38..fe11a389 100644 --- a/internal/service/notification_common/notification.go +++ b/internal/service/notification_common/notification.go @@ -73,7 +73,7 @@ func (ns *NotificationCommon) HandleNotification() { // AddNotification // need set -// UserID +// LoginUserID // Type 1 inbox 2 achievement // [inbox] Activity // [achievement] Rank diff --git a/internal/service/question_common/question.go b/internal/service/question_common/question.go index 5329c6db..451e1fa1 100644 --- a/internal/service/question_common/question.go +++ b/internal/service/question_common/question.go @@ -6,11 +6,14 @@ import ( "time" "github.com/answerdev/answer/internal/base/constant" + "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/reason" + "github.com/answerdev/answer/internal/base/translator" "github.com/answerdev/answer/internal/service/activity_common" "github.com/answerdev/answer/internal/service/activity_queue" "github.com/answerdev/answer/internal/service/config" "github.com/answerdev/answer/internal/service/meta" + "github.com/answerdev/answer/pkg/checker" "github.com/answerdev/answer/pkg/htmltext" "github.com/segmentfault/pacman/errors" @@ -30,8 +33,8 @@ type QuestionRepo interface { UpdateQuestion(ctx context.Context, question *entity.Question, Cols []string) (err error) GetQuestion(ctx context.Context, id string) (question *entity.Question, exist bool, err error) GetQuestionList(ctx context.Context, question *entity.Question) (questions []*entity.Question, err error) - GetQuestionPage(ctx context.Context, page, pageSize int, question *entity.Question) (questions []*entity.Question, total int64, err error) - SearchList(ctx context.Context, search *schema.QuestionSearch) ([]*entity.QuestionTag, int64, error) + GetQuestionPage(ctx context.Context, page, pageSize int, userID, tagID, orderCond string) ( + questionList []*entity.Question, total int64, err error) UpdateQuestionStatus(ctx context.Context, question *entity.Question) (err error) SearchByTitleLike(ctx context.Context, title string) (questionList []*entity.Question, err error) UpdatePvCount(ctx context.Context, questionID string) (err error) @@ -126,21 +129,15 @@ func (qs *QuestionCommon) UpdataPostSetTime(ctx context.Context, questionID stri func (qs *QuestionCommon) FindInfoByID(ctx context.Context, questionIDs []string, loginUserID string) (map[string]*schema.QuestionInfo, error) { list := make(map[string]*schema.QuestionInfo) - listAddTag := make([]*entity.QuestionTag, 0) questionList, err := qs.questionRepo.FindByID(ctx, questionIDs) if err != nil { return list, err } - for _, item := range questionList { - itemAddTag := &entity.QuestionTag{} - itemAddTag.Question = *item - listAddTag = append(listAddTag, itemAddTag) - } - QuestionInfo, err := qs.ListFormat(ctx, listAddTag, loginUserID) + questions, err := qs.FormatQuestions(ctx, questionList, loginUserID) if err != nil { return list, err } - for _, item := range QuestionInfo { + for _, item := range questions { list[item.ID] = item } return list, nil @@ -244,13 +241,114 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUser return showinfo, nil } -func (qs *QuestionCommon) ListFormat(ctx context.Context, questionList []*entity.QuestionTag, loginUserID string) ([]*schema.QuestionInfo, error) { +func (qs *QuestionCommon) FormatQuestionsPage( + ctx context.Context, questionList []*entity.Question, loginUserID string, orderCond string) ( + formattedQuestions []*schema.QuestionPageResp, err error) { + language := handler.GetLangByCtx(ctx) + askedOp := translator.GlobalTrans.Tr(language, schema.QuestionPageRespOperationTypeAsked) + answeredOp := translator.GlobalTrans.Tr(language, schema.QuestionPageRespOperationTypeAnswered) + modifiedOp := translator.GlobalTrans.Tr(language, schema.QuestionPageRespOperationTypeModified) + + formattedQuestions = make([]*schema.QuestionPageResp, 0) + + questionIDs := make([]string, 0) + userIDs := make([]string, 0) + for _, questionInfo := range questionList { + t := &schema.QuestionPageResp{ + ID: questionInfo.ID, + Title: questionInfo.Title, + UrlTitle: htmltext.UrlTitle(questionInfo.Title), + Description: htmltext.FetchExcerpt(questionInfo.ParsedText, "...", 240), + Status: questionInfo.Status, + ViewCount: questionInfo.ViewCount, + UniqueViewCount: questionInfo.UniqueViewCount, + VoteCount: questionInfo.VoteCount, + AnswerCount: questionInfo.AnswerCount, + CollectionCount: questionInfo.CollectionCount, + FollowCount: questionInfo.FollowCount, + AcceptedAnswerID: questionInfo.AcceptedAnswerID, + LastAnswerID: questionInfo.LastAnswerID, + } + + questionIDs = append(questionIDs, questionInfo.ID) + userIDs = append(userIDs, questionInfo.UserID) + haveEdited, haveAnswered := false, false + if checker.IsNotZeroString(questionInfo.LastEditUserID) { + haveEdited = true + userIDs = append(userIDs, questionInfo.LastEditUserID) + } + if checker.IsNotZeroString(questionInfo.LastAnswerID) { + haveAnswered = true + + answerInfo, exist, err := qs.answerRepo.GetAnswer(ctx, questionInfo.LastAnswerID) + if err == nil && exist { + if answerInfo.LastEditUserID != "0" { + t.LastAnsweredUserID = answerInfo.LastEditUserID + } else { + t.LastAnsweredUserID = answerInfo.UserID + } + t.LastAnsweredAt = answerInfo.CreatedAt + userIDs = append(userIDs, t.LastAnsweredUserID) + } + } + + // if order condition is newest or nobody edited or nobody answered, only show question author + if orderCond == schema.QuestionOrderCondNewest || (!haveEdited && !haveAnswered) { + t.OperationType = askedOp + t.OperatedAt = questionInfo.CreatedAt.Unix() + t.Operator = &schema.QuestionPageRespOperator{ID: questionInfo.UserID} + } + + // if no one + if haveEdited { + t.OperationType = modifiedOp + t.OperatedAt = questionInfo.UpdatedAt.Unix() + t.Operator = &schema.QuestionPageRespOperator{ID: questionInfo.LastEditUserID} + } + + if haveAnswered { + if t.LastAnsweredAt.Unix() > t.OperatedAt { + t.OperationType = answeredOp + t.OperatedAt = t.LastAnsweredAt.Unix() + t.Operator = &schema.QuestionPageRespOperator{ID: t.LastAnsweredUserID} + } + } + formattedQuestions = append(formattedQuestions, t) + } + + tagsMap, err := qs.tagCommon.BatchGetObjectTag(ctx, questionIDs) + if err != nil { + return formattedQuestions, err + } + userInfoMap, err := qs.userCommon.BatchUserBasicInfoByID(ctx, userIDs) + if err != nil { + return formattedQuestions, err + } + + for _, item := range formattedQuestions { + tags, ok := tagsMap[item.ID] + if ok { + item.Tags = tags + } else { + item.Tags = make([]*schema.TagResp, 0) + } + userInfo := userInfoMap[item.Operator.ID] + if userInfo != nil { + item.Operator.DisplayName = userInfo.DisplayName + item.Operator.Username = userInfo.Username + item.Operator.Rank = userInfo.Rank + } + } + return formattedQuestions, nil +} + +func (qs *QuestionCommon) FormatQuestions(ctx context.Context, questionList []*entity.Question, loginUserID string) ([]*schema.QuestionInfo, error) { list := make([]*schema.QuestionInfo, 0) objectIds := make([]string, 0) userIds := make([]string, 0) for _, questionInfo := range questionList { - item := qs.ShowListFormat(ctx, questionInfo) + item := qs.ShowFormat(ctx, questionInfo) list = append(list, item) objectIds = append(objectIds, item.ID) userIds = append(userIds, item.UserID) @@ -387,8 +485,8 @@ func (as *QuestionCommon) RemoveAnswer(ctx context.Context, id string) (err erro return as.answerRepo.RemoveAnswer(ctx, id) } -func (qs *QuestionCommon) ShowListFormat(ctx context.Context, data *entity.QuestionTag) *schema.QuestionInfo { - return qs.ShowFormat(ctx, &data.Question) +func (qs *QuestionCommon) ShowListFormat(ctx context.Context, data *entity.Question) *schema.QuestionInfo { + return qs.ShowFormat(ctx, data) } func (qs *QuestionCommon) ShowFormat(ctx context.Context, data *entity.Question) *schema.QuestionInfo { diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 71ab9123..a5b48556 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -655,12 +655,13 @@ func (qs *QuestionService) SearchUserList(ctx context.Context, userName, order s if !Exist { return userlist, 0, nil } - search := &schema.QuestionSearch{} - search.Order = order + search := &schema.QuestionPageReq{} + search.OrderCond = order search.Page = page search.PageSize = pageSize - search.UserID = userinfo.ID - questionlist, count, err := qs.SearchList(ctx, search, loginUserID) + search.UserIDBeSearched = userinfo.ID + search.LoginUserID = loginUserID + questionlist, count, err := qs.GetQuestionPage(ctx, search) if err != nil { return userlist, 0, err } @@ -778,12 +779,13 @@ func (qs *QuestionService) SearchUserTopList(ctx context.Context, userName strin if !Exist { return userQuestionlist, userAnswerlist, nil } - search := &schema.QuestionSearch{} - search.Order = "score" + search := &schema.QuestionPageReq{} + search.OrderCond = "score" search.Page = 0 search.PageSize = 5 - search.UserID = userinfo.ID - questionlist, _, err := qs.SearchList(ctx, search, loginUserID) + search.UserIDBeSearched = userinfo.ID + search.LoginUserID = loginUserID + questionlist, _, err := qs.GetQuestionPage(ctx, search) if err != nil { return userQuestionlist, userAnswerlist, err } @@ -858,57 +860,64 @@ func (qs *QuestionService) SearchByTitleLike(ctx context.Context, title string, } // SimilarQuestion -func (qs *QuestionService) SimilarQuestion(ctx context.Context, questionID string, loginUserID string) ([]*schema.QuestionInfo, int64, error) { - list := make([]*schema.QuestionInfo, 0) +func (qs *QuestionService) SimilarQuestion(ctx context.Context, questionID string, loginUserID string) ([]*schema.QuestionPageResp, int64, error) { question, err := qs.questioncommon.Info(ctx, questionID, loginUserID) if err != nil { - return list, 0, nil + return nil, 0, nil } tagNames := make([]string, 0, len(question.Tags)) for _, tag := range question.Tags { tagNames = append(tagNames, tag.SlugName) } - search := &schema.QuestionSearch{} - search.Order = "frequent" + search := &schema.QuestionPageReq{} + search.OrderCond = "frequent" search.Page = 0 search.PageSize = 6 if len(tagNames) > 0 { search.Tag = tagNames[0] } - return qs.SearchList(ctx, search, loginUserID) + search.LoginUserID = loginUserID + return qs.GetQuestionPage(ctx, search) } -// SearchList -func (qs *QuestionService) SearchList(ctx context.Context, req *schema.QuestionSearch, loginUserID string) ([]*schema.QuestionInfo, int64, error) { +// GetQuestionPage query questions page +func (qs *QuestionService) GetQuestionPage(ctx context.Context, req *schema.QuestionPageReq) ( + questions []*schema.QuestionPageResp, total int64, err error) { + questions = make([]*schema.QuestionPageResp, 0) + + // query by tag condition if len(req.Tag) > 0 { - tagInfo, has, err := qs.tagCommon.GetTagBySlugName(ctx, strings.ToLower(req.Tag)) + tagInfo, exist, err := qs.tagCommon.GetTagBySlugName(ctx, strings.ToLower(req.Tag)) if err != nil { - log.Error("tagCommon.GetTagListByNames error", err) + return nil, 0, err } - if has { - req.TagIDs = append(req.TagIDs, tagInfo.ID) + if exist { + req.TagID = tagInfo.ID } } - list := make([]*schema.QuestionInfo, 0) - if req.UserName != "" { - userinfo, exist, err := qs.userCommon.GetUserBasicInfoByUserName(ctx, req.UserName) + + // query by user condition + if req.Username != "" { + userinfo, exist, err := qs.userCommon.GetUserBasicInfoByUserName(ctx, req.Username) if err != nil { - return list, 0, err + return nil, 0, err } if !exist { - return list, 0, err + return questions, 0, nil } - req.UserID = userinfo.ID + req.UserIDBeSearched = userinfo.ID } - questionList, count, err := qs.questionRepo.SearchList(ctx, req) + + questionList, total, err := qs.questionRepo.GetQuestionPage(ctx, req.Page, req.PageSize, + req.UserIDBeSearched, req.TagID, req.OrderCond) if err != nil { - return list, count, err + return nil, 0, err } - list, err = qs.questioncommon.ListFormat(ctx, questionList, loginUserID) + questions, err = qs.questioncommon.FormatQuestionsPage(ctx, questionList, req.LoginUserID, req.OrderCond) if err != nil { - return list, count, err + return nil, 0, err } - return list, count, nil + return questions, total, nil } func (qs *QuestionService) AdminSetQuestionStatus(ctx context.Context, questionID string, setStatusStr string) error { diff --git a/pkg/checker/zero_string.go b/pkg/checker/zero_string.go new file mode 100644 index 00000000..a318af28 --- /dev/null +++ b/pkg/checker/zero_string.go @@ -0,0 +1,6 @@ +package checker + +// IsNotZeroString check s is not empty string and is not "0" +func IsNotZeroString(s string) bool { + return len(s) > 0 && s != "0" +} diff --git a/ui/template/question.html b/ui/template/question.html index cad6d1a8..8b4347b8 100644 --- a/ui/template/question.html +++ b/ui/template/question.html @@ -25,31 +25,21 @@ >
- {{.UserInfo.DisplayName}}{{.Operator.DisplayName}}{{.UserInfo.Rank}}{{.Operator.Rank}}
- • {{if eq .CreateTime .UpdateTime}} - {{else if gt .UpdateTime 0}} - - {{end}}