answer/internal/controller/question_controller.go

865 lines
28 KiB
Go
Raw Normal View History

2022-09-27 17:59:05 +08:00
package controller
import (
"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"
2023-05-25 18:12:32 +08:00
"github.com/answerdev/answer/internal/base/translator"
2022-12-13 18:10:35 +08:00
"github.com/answerdev/answer/internal/base/validator"
2023-07-19 17:30:27 +08:00
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service"
2023-07-19 17:30:27 +08:00
"github.com/answerdev/answer/internal/service/action"
"github.com/answerdev/answer/internal/service/permission"
"github.com/answerdev/answer/internal/service/rank"
"github.com/answerdev/answer/internal/service/siteinfo_common"
2023-03-09 14:39:32 +08:00
"github.com/answerdev/answer/pkg/uid"
2022-09-27 17:59:05 +08:00
"github.com/gin-gonic/gin"
2023-04-04 16:54:28 +08:00
"github.com/jinzhu/copier"
2022-09-27 17:59:05 +08:00
"github.com/segmentfault/pacman/errors"
)
// QuestionController question controller
type QuestionController struct {
questionService *service.QuestionService
2023-04-04 16:54:28 +08:00
answerService *service.AnswerService
2022-09-27 17:59:05 +08:00
rankService *rank.RankService
siteInfoService siteinfo_common.SiteInfoCommonService
2023-07-19 17:30:27 +08:00
actionService *action.CaptchaService
2022-09-27 17:59:05 +08:00
}
// NewQuestionController new controller
2023-04-04 16:54:28 +08:00
func NewQuestionController(
questionService *service.QuestionService,
answerService *service.AnswerService,
rankService *rank.RankService,
siteInfoService siteinfo_common.SiteInfoCommonService,
2023-07-19 17:30:27 +08:00
actionService *action.CaptchaService,
2023-04-04 16:54:28 +08:00
) *QuestionController {
return &QuestionController{
questionService: questionService,
answerService: answerService,
rankService: rankService,
siteInfoService: siteInfoService,
2023-07-19 17:30:27 +08:00
actionService: actionService,
2023-04-04 16:54:28 +08:00
}
2022-09-27 17:59:05 +08:00
}
// RemoveQuestion delete question
// @Summary delete question
// @Description delete question
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.RemoveQuestionReq true "question"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question [delete]
func (qc *QuestionController) RemoveQuestion(ctx *gin.Context) {
req := &schema.RemoveQuestionReq{}
if handler.BindAndCheck(ctx, req) {
return
}
2023-03-09 14:39:32 +08:00
req.ID = uid.DeShortID(req.ID)
2022-09-27 17:59:05 +08:00
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
2023-07-19 18:42:12 +08:00
isAdmin := middleware.GetUserIsAdminModerator(ctx)
if !isAdmin {
captchaPass := qc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionDelete, req.UserID, req.CaptchaID, req.CaptchaCode)
if !captchaPass {
errFields := append([]*validator.FormErrorField{}, &validator.FormErrorField{
ErrorField: "captcha_code",
ErrorMsg: translator.Tr(handler.GetLang(ctx), reason.CaptchaVerificationFailed),
})
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), errFields)
return
}
2023-07-19 17:46:14 +08:00
}
can, err := qc.rankService.CheckOperationPermission(ctx, req.UserID, permission.QuestionDelete, req.ID)
2022-11-24 18:00:30 +08:00
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !can {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
2022-09-27 17:59:05 +08:00
return
}
2022-11-24 18:00:30 +08:00
err = qc.questionService.RemoveQuestion(ctx, req)
2023-07-19 18:42:12 +08:00
if !isAdmin {
qc.actionService.ActionRecordAdd(ctx, entity.CaptchaActionDelete, req.UserID)
}
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, err, nil)
}
2023-04-13 11:17:17 +08:00
// OperationQuestion Operation question
// @Summary Operation question
// @Description Operation question \n operation [pin unpin hide show]
// @Tags Question
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.OperationQuestionReq true "question"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question/operation [put]
func (qc *QuestionController) OperationQuestion(ctx *gin.Context) {
req := &schema.OperationQuestionReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.ID = uid.DeShortID(req.ID)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
permission.QuestionPin,
2023-04-13 18:20:26 +08:00
permission.QuestionUnPin,
2023-04-13 11:17:17 +08:00
permission.QuestionHide,
2023-04-13 18:20:26 +08:00
permission.QuestionShow,
2023-04-13 11:17:17 +08:00
})
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
req.CanPin = canList[0]
req.CanList = canList[1]
if (req.Operation == schema.QuestionOperationPin || req.Operation == schema.QuestionOperationUnPin) && !req.CanPin {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
if (req.Operation == schema.QuestionOperationHide || req.Operation == schema.QuestionOperationShow) && !req.CanList {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
2023-04-13 17:34:02 +08:00
err = qc.questionService.OperationQuestion(ctx, req)
handler.HandleResponse(ctx, err, nil)
2023-04-13 11:17:17 +08:00
}
2022-09-27 17:59:05 +08:00
// CloseQuestion Close question
// @Summary Close question
// @Description Close question
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.CloseQuestionReq true "question"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question/status [put]
func (qc *QuestionController) CloseQuestion(ctx *gin.Context) {
req := &schema.CloseQuestionReq{}
if handler.BindAndCheck(ctx, req) {
return
}
2023-03-16 15:22:47 +08:00
req.ID = uid.DeShortID(req.ID)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
can, err := qc.rankService.CheckOperationPermission(ctx, req.UserID, permission.QuestionClose, "")
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !can {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
err = qc.questionService.CloseQuestion(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// ReopenQuestion reopen question
// @Summary reopen question
// @Description reopen question
// @Tags Question
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.ReopenQuestionReq true "question"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question/reopen [put]
func (qc *QuestionController) ReopenQuestion(ctx *gin.Context) {
req := &schema.ReopenQuestionReq{}
if handler.BindAndCheck(ctx, req) {
return
}
2023-03-16 15:22:47 +08:00
req.QuestionID = uid.DeShortID(req.QuestionID)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
can, err := qc.rankService.CheckOperationPermission(ctx, req.UserID, permission.QuestionReopen, "")
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !can {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
err = qc.questionService.ReopenQuestion(ctx, req)
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, err, nil)
}
// GetQuestion get question details
// @Summary get question details
// @Description get question details
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Security ApiKeyAuth
// @Accept json
// @Produce json
// @Param id query string true "Question TagID" default(1)
// @Success 200 {string} string ""
// @Router /answer/api/v1/question/info [get]
func (qc *QuestionController) GetQuestion(ctx *gin.Context) {
id := ctx.Query("id")
2023-03-16 15:22:47 +08:00
id = uid.DeShortID(id)
userID := middleware.GetLoginUserIDFromContext(ctx)
req := schema.QuestionPermission{}
canList, err := qc.rankService.CheckOperationPermissions(ctx, userID, []string{
permission.QuestionEdit,
permission.QuestionDelete,
permission.QuestionClose,
permission.QuestionReopen,
2023-04-13 14:39:27 +08:00
permission.QuestionPin,
2023-04-13 18:20:26 +08:00
permission.QuestionUnPin,
2023-04-13 14:39:27 +08:00
permission.QuestionHide,
2023-04-13 18:20:26 +08:00
permission.QuestionShow,
permission.AnswerInviteSomeoneToAnswer,
})
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
objectOwner := qc.rankService.CheckOperationObjectOwner(ctx, userID, id)
req.CanEdit = canList[0] || objectOwner
req.CanDelete = canList[1]
req.CanClose = canList[2]
req.CanReopen = canList[3]
2023-04-13 14:39:27 +08:00
req.CanPin = canList[4]
2023-04-13 18:20:26 +08:00
req.CanUnPin = canList[5]
req.CanHide = canList[6]
req.CanShow = canList[7]
req.CanInviteOtherToAnswer = canList[8]
info, err := qc.questionService.GetQuestionAndAddPV(ctx, id, userID, req)
2022-09-27 17:59:05 +08:00
if err != nil {
handler.HandleResponse(ctx, err, nil)
2022-09-27 17:59:05 +08:00
return
}
if handler.GetEnableShortID(ctx) {
info.ID = uid.EnShortID(info.ID)
}
handler.HandleResponse(ctx, nil, info)
2022-09-27 17:59:05 +08:00
}
2023-05-23 16:00:19 +08:00
// GetQuestionInviteUserInfo get question invite user info
// @Summary get question invite user info
// @Description get question invite user info
// @Tags Question
// @Security ApiKeyAuth
// @Accept json
// @Produce json
// @Param id query string true "Question ID" default(1)
// @Success 200 {string} string ""
2023-05-23 16:07:43 +08:00
// @Router /answer/api/v1/question/invite [get]
2023-05-23 16:00:19 +08:00
func (qc *QuestionController) GetQuestionInviteUserInfo(ctx *gin.Context) {
questionID := uid.DeShortID(ctx.Query("id"))
resp, err := qc.questionService.InviteUserInfo(ctx, questionID)
handler.HandleResponse(ctx, err, resp)
2023-05-23 16:00:19 +08:00
}
2022-09-27 17:59:05 +08:00
// SimilarQuestion godoc
// @Summary Search Similar Question
// @Description Search Similar Question
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Param question_id query string true "question_id" default()
// @Success 200 {string} string ""
// @Router /answer/api/v1/question/similar/tag [get]
func (qc *QuestionController) SimilarQuestion(ctx *gin.Context) {
questionID := ctx.Query("question_id")
2023-03-16 15:22:47 +08:00
questionID = uid.DeShortID(questionID)
2022-09-27 17:59:05 +08:00
userID := middleware.GetLoginUserIDFromContext(ctx)
list, count, err := qc.questionService.SimilarQuestion(ctx, questionID, userID)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
handler.HandleResponse(ctx, nil, gin.H{
"list": list,
"count": count,
})
}
// QuestionPage get questions by page
// @Summary get questions by page
// @Description get questions by page
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Param data body schema.QuestionPageReq true "QuestionPageReq"
// @Success 200 {object} handler.RespBody{data=pager.PageModel{list=[]schema.QuestionPageResp}}
2022-10-12 17:12:17 +08:00
// @Router /answer/api/v1/question/page [get]
func (qc *QuestionController) QuestionPage(ctx *gin.Context) {
req := &schema.QuestionPageReq{}
2022-09-27 17:59:05 +08:00
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
2022-09-27 17:59:05 +08:00
questions, total, err := qc.questionService.GetQuestionPage(ctx, req)
2022-09-27 17:59:05 +08:00
if err != nil {
handler.HandleResponse(ctx, err, nil)
2022-09-27 17:59:05 +08:00
return
}
handler.HandleResponse(ctx, nil, pager.NewPageModel(total, questions))
2022-09-27 17:59:05 +08:00
}
// AddQuestion add question
// @Summary add question
// @Description add question
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.QuestionAdd true "question"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question [post]
func (qc *QuestionController) AddQuestion(ctx *gin.Context) {
req := &schema.QuestionAdd{}
2022-12-13 18:10:35 +08:00
errFields := handler.BindAndCheckReturnErr(ctx, req)
if ctx.IsAborted() {
2022-09-27 17:59:05 +08:00
return
}
2023-07-19 17:30:27 +08:00
2023-08-01 15:01:28 +08:00
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
2023-05-25 18:12:32 +08:00
canList, requireRanks, err := qc.rankService.CheckOperationPermissionsForRanks(ctx, req.UserID, []string{
permission.QuestionAdd,
permission.QuestionEdit,
permission.QuestionDelete,
permission.QuestionClose,
permission.QuestionReopen,
permission.TagUseReservedTag,
2023-05-25 18:12:32 +08:00
permission.TagAdd,
2023-08-01 15:01:28 +08:00
permission.LinkUrlLimit,
})
2022-11-24 18:00:30 +08:00
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
2023-08-01 15:01:28 +08:00
linkUrlLimitUser := canList[7]
isAdmin := middleware.GetUserIsAdminModerator(ctx)
if !isAdmin || !linkUrlLimitUser {
captchaPass := qc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionQuestion, req.UserID, req.CaptchaID, req.CaptchaCode)
if !captchaPass {
errFields := append([]*validator.FormErrorField{}, &validator.FormErrorField{
ErrorField: "captcha_code",
ErrorMsg: translator.Tr(handler.GetLang(ctx), reason.CaptchaVerificationFailed),
})
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), errFields)
return
}
}
req.CanAdd = canList[0]
req.CanEdit = canList[1]
req.CanDelete = canList[2]
req.CanClose = canList[3]
req.CanReopen = canList[4]
req.CanUseReservedTag = canList[5]
2023-05-25 18:12:32 +08:00
req.CanAddTag = canList[6]
if !req.CanAdd {
2022-11-24 18:00:30 +08:00
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
2022-09-27 17:59:05 +08:00
return
}
2023-05-25 18:12:32 +08:00
// can add tag
hasNewTag, err := qc.questionService.HasNewTag(ctx, req.Tags)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !req.CanAddTag && hasNewTag {
lang := handler.GetLang(ctx)
msg := translator.TrWithData(lang, reason.NoEnoughRankToOperate, &schema.PermissionTrTplData{Rank: requireRanks[6]})
handler.HandleResponse(ctx, errors.Forbidden(reason.NoEnoughRankToOperate).WithMsg(msg), nil)
return
}
2022-12-22 11:40:56 +08:00
errList, err := qc.questionService.CheckAddQuestion(ctx, req)
if err != nil {
errlist, ok := errList.([]*validator.FormErrorField)
if ok {
errFields = append(errFields, errlist...)
}
}
if len(errFields) > 0 {
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
return
}
2022-09-27 17:59:05 +08:00
resp, err := qc.questionService.AddQuestion(ctx, req)
2022-12-13 18:10:35 +08:00
if err != nil {
errlist, ok := resp.([]*validator.FormErrorField)
if ok {
errFields = append(errFields, errlist...)
2022-12-13 18:10:35 +08:00
}
}
2022-12-22 11:40:56 +08:00
2022-12-13 18:10:35 +08:00
if len(errFields) > 0 {
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
return
}
2023-08-01 15:01:28 +08:00
if !isAdmin || !linkUrlLimitUser {
2023-07-19 18:42:12 +08:00
qc.actionService.ActionRecordAdd(ctx, entity.CaptchaActionQuestion, req.UserID)
}
2022-09-27 17:59:05 +08:00
handler.HandleResponse(ctx, err, resp)
}
2023-04-04 16:54:28 +08:00
// AddQuestionByAnswer add question
// @Summary add question and answer
// @Description add question and answer
// @Tags Question
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.QuestionAddByAnswer true "question"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question/answer [post]
func (qc *QuestionController) AddQuestionByAnswer(ctx *gin.Context) {
req := &schema.QuestionAddByAnswer{}
errFields := handler.BindAndCheckReturnErr(ctx, req)
if ctx.IsAborted() {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
permission.QuestionAdd,
permission.QuestionEdit,
permission.QuestionDelete,
permission.QuestionClose,
permission.QuestionReopen,
permission.TagUseReservedTag,
2023-08-01 15:01:28 +08:00
permission.LinkUrlLimit,
2023-04-04 16:54:28 +08:00
})
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
2023-08-01 15:01:28 +08:00
linkUrlLimitUser := canList[6]
isAdmin := middleware.GetUserIsAdminModerator(ctx)
if !isAdmin || !linkUrlLimitUser {
captchaPass := qc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionQuestion, req.UserID, req.CaptchaID, req.CaptchaCode)
if !captchaPass {
errFields := append([]*validator.FormErrorField{}, &validator.FormErrorField{
ErrorField: "captcha_code",
ErrorMsg: translator.Tr(handler.GetLang(ctx), reason.CaptchaVerificationFailed),
})
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), errFields)
return
}
}
2023-04-04 16:54:28 +08:00
req.CanAdd = canList[0]
req.CanEdit = canList[1]
req.CanDelete = canList[2]
req.CanClose = canList[3]
req.CanReopen = canList[4]
req.CanUseReservedTag = canList[5]
if !req.CanAdd {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
questionReq := new(schema.QuestionAdd)
err = copier.Copy(questionReq, req)
if err != nil {
handler.HandleResponse(ctx, errors.Forbidden(reason.RequestFormatError), nil)
return
}
errList, err := qc.questionService.CheckAddQuestion(ctx, questionReq)
if err != nil {
errlist, ok := errList.([]*validator.FormErrorField)
if ok {
errFields = append(errFields, errlist...)
}
}
if len(errFields) > 0 {
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
return
}
resp, err := qc.questionService.AddQuestion(ctx, questionReq)
if err != nil {
errlist, ok := resp.([]*validator.FormErrorField)
if ok {
errFields = append(errFields, errlist...)
}
}
2023-08-01 15:01:28 +08:00
if !isAdmin || !linkUrlLimitUser {
qc.actionService.ActionRecordAdd(ctx, entity.CaptchaActionQuestion, req.UserID)
}
2023-04-04 16:54:28 +08:00
if len(errFields) > 0 {
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
return
}
//add the question id to the answer
questionInfo, ok := resp.(*schema.QuestionInfo)
if ok {
answerReq := &schema.AnswerAddReq{}
answerReq.QuestionID = uid.DeShortID(questionInfo.ID)
answerReq.UserID = middleware.GetLoginUserIDFromContext(ctx)
answerReq.Content = req.AnswerContent
answerReq.HTML = req.AnswerHTML
answerID, err := qc.answerService.Insert(ctx, answerReq)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
info, questionInfo, has, err := qc.answerService.Get(ctx, answerID, req.UserID)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !has {
handler.HandleResponse(ctx, nil, nil)
return
}
handler.HandleResponse(ctx, err, gin.H{
"info": info,
"question": questionInfo,
})
return
}
handler.HandleResponse(ctx, err, resp)
}
2022-09-27 17:59:05 +08:00
// UpdateQuestion update question
// @Summary update question
// @Description update question
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.QuestionUpdate true "question"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question [put]
func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
req := &schema.QuestionUpdate{}
2022-12-09 10:55:53 +08:00
errFields := handler.BindAndCheckReturnErr(ctx, req)
if ctx.IsAborted() {
2022-09-27 17:59:05 +08:00
return
}
2023-03-16 15:22:47 +08:00
req.ID = uid.DeShortID(req.ID)
2022-09-27 17:59:05 +08:00
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
2023-05-25 18:12:32 +08:00
canList, requireRanks, err := qc.rankService.CheckOperationPermissionsForRanks(ctx, req.UserID, []string{
permission.QuestionEdit,
permission.QuestionDelete,
permission.QuestionEditWithoutReview,
permission.TagUseReservedTag,
2023-05-25 18:12:32 +08:00
permission.TagAdd,
2023-08-01 15:01:28 +08:00
permission.LinkUrlLimit,
})
2022-11-24 18:00:30 +08:00
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
2023-08-01 15:01:28 +08:00
linkUrlLimitUser := canList[5]
isAdmin := middleware.GetUserIsAdminModerator(ctx)
if !isAdmin || !linkUrlLimitUser {
captchaPass := qc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionEdit, req.UserID, req.CaptchaID, req.CaptchaCode)
if !captchaPass {
errFields := append([]*validator.FormErrorField{}, &validator.FormErrorField{
ErrorField: "captcha_code",
ErrorMsg: translator.Tr(handler.GetLang(ctx), reason.CaptchaVerificationFailed),
})
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), errFields)
return
}
}
objectOwner := qc.rankService.CheckOperationObjectOwner(ctx, req.UserID, req.ID)
req.CanEdit = canList[0] || objectOwner
req.CanDelete = canList[1]
req.NoNeedReview = canList[2] || objectOwner
req.CanUseReservedTag = canList[3]
2023-05-25 18:12:32 +08:00
req.CanAddTag = canList[4]
if !req.CanEdit {
2022-11-24 18:00:30 +08:00
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
2022-09-27 17:59:05 +08:00
return
}
2022-12-09 17:52:11 +08:00
errlist, err := qc.questionService.UpdateQuestionCheckTags(ctx, req)
if err != nil {
errFields = append(errFields, errlist...)
2022-12-09 17:52:11 +08:00
}
if len(errFields) > 0 {
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
return
}
2023-05-25 18:12:32 +08:00
// can add tag
hasNewTag, err := qc.questionService.HasNewTag(ctx, req.Tags)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !req.CanAddTag && hasNewTag {
lang := handler.GetLang(ctx)
msg := translator.TrWithData(lang, reason.NoEnoughRankToOperate, &schema.PermissionTrTplData{Rank: requireRanks[4]})
handler.HandleResponse(ctx, errors.Forbidden(reason.NoEnoughRankToOperate).WithMsg(msg), nil)
return
}
resp, err := qc.questionService.UpdateQuestion(ctx, req)
if err != nil {
handler.HandleResponse(ctx, err, resp)
return
}
2023-08-01 15:01:28 +08:00
if !isAdmin || !linkUrlLimitUser {
2023-07-19 18:42:12 +08:00
qc.actionService.ActionRecordAdd(ctx, entity.CaptchaActionEdit, req.UserID)
}
handler.HandleResponse(ctx, nil, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview})
2022-09-27 17:59:05 +08:00
}
2023-05-23 16:00:19 +08:00
// UpdateQuestionInviteUser update question invite user
// @Summary update question invite user
// @Description update question invite user
// @Tags Question
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.QuestionUpdateInviteUser true "question"
// @Success 200 {object} handler.RespBody
2023-05-23 16:07:43 +08:00
// @Router /answer/api/v1/question/invite [put]
2023-05-23 16:00:19 +08:00
func (qc *QuestionController) UpdateQuestionInviteUser(ctx *gin.Context) {
req := &schema.QuestionUpdateInviteUser{}
errFields := handler.BindAndCheckReturnErr(ctx, req)
if ctx.IsAborted() {
return
}
if len(errFields) > 0 {
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
return
}
2023-05-23 16:00:19 +08:00
req.ID = uid.DeShortID(req.ID)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
2023-07-19 18:42:12 +08:00
isAdmin := middleware.GetUserIsAdminModerator(ctx)
if !isAdmin {
captchaPass := qc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionInvitationAnswer, req.UserID, req.CaptchaID, req.CaptchaCode)
if !captchaPass {
errFields := append([]*validator.FormErrorField{}, &validator.FormErrorField{
ErrorField: "captcha_code",
ErrorMsg: translator.Tr(handler.GetLang(ctx), reason.CaptchaVerificationFailed),
})
handler.HandleResponse(ctx, errors.BadRequest(reason.CaptchaVerificationFailed), errFields)
return
}
2023-07-19 17:46:14 +08:00
}
2023-05-23 16:00:19 +08:00
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
permission.AnswerInviteSomeoneToAnswer,
2023-05-23 16:00:19 +08:00
})
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
req.CanInviteOtherToAnswer = canList[0]
if !req.CanInviteOtherToAnswer {
2023-05-23 16:00:19 +08:00
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
err = qc.questionService.UpdateQuestionInviteUser(ctx, req)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
2023-07-19 18:42:12 +08:00
if !isAdmin {
qc.actionService.ActionRecordAdd(ctx, entity.CaptchaActionInvitationAnswer, req.UserID)
}
2023-05-23 16:00:19 +08:00
handler.HandleResponse(ctx, nil, nil)
}
2022-09-27 17:59:05 +08:00
// SearchByTitleLike add question title like
// @Summary add question title like
// @Description add question title like
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param title query string true "title" default(string)
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question/similar [get]
func (qc *QuestionController) SearchByTitleLike(ctx *gin.Context) {
title := ctx.Query("title")
userID := middleware.GetLoginUserIDFromContext(ctx)
resp, err := qc.questionService.SearchByTitleLike(ctx, title, userID)
handler.HandleResponse(ctx, err, resp)
}
// UserTop godoc
// @Summary UserTop
// @Description UserTop
// @Tags Question
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param username query string true "username" default(string)
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/personal/qa/top [get]
func (qc *QuestionController) UserTop(ctx *gin.Context) {
userName := ctx.Query("username")
userID := middleware.GetLoginUserIDFromContext(ctx)
questionList, answerList, err := qc.questionService.SearchUserTopList(ctx, userName, userID)
handler.HandleResponse(ctx, err, gin.H{
"question": questionList,
"answer": answerList,
})
}
// PersonalQuestionPage list personal questions
// @Summary list personal questions
// @Description list personal questions
// @Tags Personal
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param username query string true "username" default(string)
// @Param order query string true "order" Enums(newest,score)
// @Param page query string true "page" default(0)
// @Param page_size query string true "page_size" default(20)
2022-09-27 17:59:05 +08:00
// @Success 200 {object} handler.RespBody
// @Router /personal/question/page [get]
func (qc *QuestionController) PersonalQuestionPage(ctx *gin.Context) {
req := &schema.PersonalQuestionPageReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
resp, err := qc.questionService.PersonalQuestionPage(ctx, req)
handler.HandleResponse(ctx, err, resp)
2022-09-27 17:59:05 +08:00
}
// PersonalAnswerPage list personal answers
// @Summary list personal answers
// @Description list personal answers
// @Tags Personal
2022-09-27 17:59:05 +08:00
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param username query string true "username" default(string)
// @Param order query string true "order" Enums(newest,score)
// @Param page query string true "page" default(0)
// @Param page_size query string true "page_size" default(20)
2022-09-27 17:59:05 +08:00
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/personal/answer/page [get]
func (qc *QuestionController) PersonalAnswerPage(ctx *gin.Context) {
req := &schema.PersonalAnswerPageReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
resp, err := qc.questionService.PersonalAnswerPage(ctx, req)
handler.HandleResponse(ctx, err, resp)
2022-09-27 17:59:05 +08:00
}
// PersonalCollectionPage list personal collections
// @Summary list personal collections
// @Description list personal collections
2022-09-27 17:59:05 +08:00
// @Tags Collection
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param page query string true "page" default(0)
// @Param page_size query string true "page_size" default(20)
2022-09-27 17:59:05 +08:00
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/personal/collection/page [get]
func (qc *QuestionController) PersonalCollectionPage(ctx *gin.Context) {
req := &schema.PersonalCollectionPageReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
resp, err := qc.questionService.PersonalCollectionPage(ctx, req)
handler.HandleResponse(ctx, err, resp)
2022-09-27 17:59:05 +08:00
}
// AdminQuestionPage admin question page
// @Summary AdminQuestionPage admin question page
2022-09-27 17:59:05 +08:00
// @Description Status:[available,closed,deleted]
// @Tags admin
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param page query int false "page size"
// @Param page_size query int false "page size"
// @Param status query string false "user status" Enums(available, closed, deleted)
// @Param query query string false "question id or title"
2022-09-27 17:59:05 +08:00
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/question/page [get]
func (qc *QuestionController) AdminQuestionPage(ctx *gin.Context) {
req := &schema.AdminQuestionPageReq{}
2022-09-27 17:59:05 +08:00
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
resp, err := qc.questionService.AdminQuestionPage(ctx, req)
handler.HandleResponse(ctx, err, resp)
2022-09-27 17:59:05 +08:00
}
// AdminAnswerPage admin answer page
// @Summary AdminAnswerPage admin answer page
2022-09-27 17:59:05 +08:00
// @Description Status:[available,deleted]
// @Tags admin
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param page query int false "page size"
// @Param page_size query int false "page size"
// @Param status query string false "user status" Enums(available,deleted)
// @Param query query string false "answer id or question title"
// @Param question_id query string false "question id"
2022-09-27 17:59:05 +08:00
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/answer/page [get]
func (qc *QuestionController) AdminAnswerPage(ctx *gin.Context) {
req := &schema.AdminAnswerPageReq{}
2022-09-27 17:59:05 +08:00
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
resp, err := qc.questionService.AdminAnswerPage(ctx, req)
handler.HandleResponse(ctx, err, resp)
2022-09-27 17:59:05 +08:00
}
// AdminSetQuestionStatus godoc
// @Summary AdminSetQuestionStatus
// @Description Status:[available,closed,deleted]
// @Tags admin
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.AdminSetQuestionStatusRequest true "AdminSetQuestionStatusRequest"
// @Router /answer/admin/api/question/status [put]
// @Success 200 {object} handler.RespBody
func (qc *QuestionController) AdminSetQuestionStatus(ctx *gin.Context) {
req := &schema.AdminSetQuestionStatusRequest{}
if handler.BindAndCheck(ctx, req) {
return
}
2023-03-16 15:22:47 +08:00
req.QuestionID = uid.DeShortID(req.QuestionID)
2022-09-27 17:59:05 +08:00
err := qc.questionService.AdminSetQuestionStatus(ctx, req.QuestionID, req.StatusStr)
handler.HandleResponse(ctx, err, gin.H{})
}