add question operation

This commit is contained in:
aichy126 2023-04-13 11:17:17 +08:00
parent 5361f4e127
commit b0d2cdc4a0
9 changed files with 256 additions and 3 deletions

View File

@ -3168,6 +3168,45 @@ const docTemplate = `{
} }
} }
}, },
"/answer/api/v1/question/operation": {
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Operation question \\n operation [pin unpin hide show]",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Question"
],
"summary": "Operation question",
"parameters": [
{
"description": "question",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.OperationQuestionReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/api/v1/question/page": { "/answer/api/v1/question/page": {
"get": { "get": {
"description": "get questions by page", "description": "get questions by page",
@ -6739,6 +6778,21 @@ const docTemplate = `{
} }
} }
}, },
"schema.OperationQuestionReq": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
},
"operation": {
"description": "operation [pin unpin hide show]",
"type": "string"
}
}
},
"schema.PermissionMemberAction": { "schema.PermissionMemberAction": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -3156,6 +3156,45 @@
} }
} }
}, },
"/answer/api/v1/question/operation": {
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Operation question \\n operation [pin unpin hide show]",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Question"
],
"summary": "Operation question",
"parameters": [
{
"description": "question",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.OperationQuestionReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/api/v1/question/page": { "/answer/api/v1/question/page": {
"get": { "get": {
"description": "get questions by page", "description": "get questions by page",
@ -6727,6 +6766,21 @@
} }
} }
}, },
"schema.OperationQuestionReq": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
},
"operation": {
"description": "operation [pin unpin hide show]",
"type": "string"
}
}
},
"schema.PermissionMemberAction": { "schema.PermissionMemberAction": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -983,6 +983,16 @@ definitions:
description: inbox achievement description: inbox achievement
type: string type: string
type: object type: object
schema.OperationQuestionReq:
properties:
id:
type: string
operation:
description: operation [pin unpin hide show]
type: string
required:
- id
type: object
schema.PermissionMemberAction: schema.PermissionMemberAction:
properties: properties:
action: action:
@ -3888,6 +3898,30 @@ paths:
summary: get question details summary: get question details
tags: tags:
- Question - Question
/answer/api/v1/question/operation:
put:
consumes:
- application/json
description: Operation question \n operation [pin unpin hide show]
parameters:
- description: question
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.OperationQuestionReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: Operation question
tags:
- Question
/answer/api/v1/question/page: /answer/api/v1/question/page:
get: get:
consumes: consumes:

View File

@ -70,6 +70,45 @@ func (qc *QuestionController) RemoveQuestion(ctx *gin.Context) {
handler.HandleResponse(ctx, err, nil) handler.HandleResponse(ctx, err, nil)
} }
// 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,
permission.QuestionHide,
})
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
}
handler.HandleResponse(ctx, nil, nil)
}
// CloseQuestion Close question // CloseQuestion Close question
// @Summary Close question // @Summary Close question
// @Description Close question // @Description Close question

View File

@ -56,6 +56,7 @@ var migrations = []Migration{
NewMigration("add user role", addRoleFeatures, false), NewMigration("add user role", addRoleFeatures, false),
NewMigration("add theme and private mode", addThemeAndPrivateMode, true), NewMigration("add theme and private mode", addThemeAndPrivateMode, true),
NewMigration("add new answer notification", addNewAnswerNotification, true), NewMigration("add new answer notification", addNewAnswerNotification, true),
NewMigration("add user pin hide features", addRolePinAndHideFeatures, true),
} }
// GetCurrentDBVersion returns the current db version // GetCurrentDBVersion returns the current db version

56
internal/migrations/v8.go Normal file
View File

@ -0,0 +1,56 @@
package migrations
import (
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/permission"
"xorm.io/xorm"
)
func addRolePinAndHideFeatures(x *xorm.Engine) error {
powers := []*entity.Power{
{ID: 34, Name: "question pin", PowerType: permission.QuestionPin, Description: "Top or untop the question"},
{ID: 35, Name: "question hide", PowerType: permission.QuestionHide, Description: "hide or show the question"},
}
// insert default powers
for _, power := range powers {
exist, err := x.Get(&entity.Power{ID: power.ID})
if err != nil {
return err
}
if exist {
_, err = x.ID(power.ID).Update(power)
} else {
_, err = x.Insert(power)
}
if err != nil {
return err
}
}
rolePowerRels := []*entity.RolePowerRel{
{RoleID: 2, PowerType: permission.QuestionPin},
{RoleID: 2, PowerType: permission.QuestionHide},
{RoleID: 3, PowerType: permission.QuestionPin},
{RoleID: 3, PowerType: permission.QuestionHide},
}
// insert default powers
for _, rel := range rolePowerRels {
exist, err := x.Get(&entity.RolePowerRel{RoleID: rel.RoleID, PowerType: rel.PowerType})
if err != nil {
return err
}
if exist {
continue
}
_, err = x.Insert(rel)
if err != nil {
return err
}
}
return nil
}

View File

@ -190,6 +190,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
r.PUT("/question", a.questionController.UpdateQuestion) r.PUT("/question", a.questionController.UpdateQuestion)
r.DELETE("/question", a.questionController.RemoveQuestion) r.DELETE("/question", a.questionController.RemoveQuestion)
r.PUT("/question/status", a.questionController.CloseQuestion) r.PUT("/question/status", a.questionController.CloseQuestion)
r.PUT("/question/operation", a.questionController.OperationQuestion)
r.PUT("/question/reopen", a.questionController.ReopenQuestion) r.PUT("/question/reopen", a.questionController.ReopenQuestion)
r.GET("/question/similar", a.questionController.SearchByTitleLike) r.GET("/question/similar", a.questionController.SearchByTitleLike)

View File

@ -8,9 +8,13 @@ import (
) )
const ( const (
SitemapMaxSize = 50000 SitemapMaxSize = 50000
SitemapCachekey = "answer@sitemap" SitemapCachekey = "answer@sitemap"
SitemapPageCachekey = "answer@sitemap@page%d" SitemapPageCachekey = "answer@sitemap@page%d"
QuestionOperationPin = "pin"
QuestionOperationUnPin = "unpin"
QuestionOperationHide = "hide"
QuestionOperationShow = "show"
) )
// RemoveQuestionReq delete question request // RemoveQuestionReq delete question request
@ -28,6 +32,14 @@ type CloseQuestionReq struct {
UserID string `json:"-"` // user_id UserID string `json:"-"` // user_id
} }
type OperationQuestionReq struct {
ID string `validate:"required" json:"id"`
Operation string `json:"operation"` // operation [pin unpin hide show]
UserID string `json:"-"` // user_id
CanPin bool `json:"-"`
CanList bool `json:"-"`
}
type CloseQuestionMeta struct { type CloseQuestionMeta struct {
CloseType int `json:"close_type"` CloseType int `json:"close_type"`
CloseMsg string `json:"close_msg"` CloseMsg string `json:"close_msg"`

View File

@ -10,6 +10,8 @@ const (
QuestionReopen = "question.reopen" QuestionReopen = "question.reopen"
QuestionVoteUp = "question.vote_up" QuestionVoteUp = "question.vote_up"
QuestionVoteDown = "question.vote_down" QuestionVoteDown = "question.vote_down"
QuestionPin = "question.pin" //Top or untop the question
QuestionHide = "question.hide" //hide or show the question
AnswerAdd = "answer.add" AnswerAdd = "answer.add"
AnswerEdit = "answer.edit" AnswerEdit = "answer.edit"
AnswerEditWithoutReview = "answer.edit_without_review" AnswerEditWithoutReview = "answer.edit_without_review"