Merge branch 'feat/1.0.8/tag' into test

This commit is contained in:
aichy126 2023-04-04 16:54:49 +08:00
commit d69afe50ec
12 changed files with 353 additions and 14 deletions

View File

@ -15,7 +15,7 @@ builds:
- id: build
main: ./cmd/answer/.
binary: answer
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.Time={{.Date}} -X main.BuildUser=goreleaser
ldflags: -s -w -X github.com/answerdev/answer/cmd.Version={{.Version}} -X github.com/answerdev/answer/cmd.Revision={{.ShortCommit}} -X github.com/answerdev/answer/cmd.Time={{.Date}} -X main.BuildUser=goreleaser
flags: -v
goos:
- linux
@ -26,7 +26,7 @@ builds:
- id: build-windows
main: ./cmd/answer/.
binary: answer
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.Time={{.Date}} -X main.BuildUser=goreleaser
ldflags: -s -w -X github.com/answerdev/answer/cmd.Version={{.Version}} -X github.com/answerdev/answer/cmd.Revision={{.ShortCommit}} -X github.com/answerdev/answer/cmd.Time={{.Date}} -X main.BuildUser=goreleaser
flags: -v
goos:
- windows

View File

@ -165,8 +165,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
questionActivityRepo := activity.NewQuestionActivityRepo(dataData, activityRepo, userRankRepo)
answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, questionActivityRepo)
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, revisionService, metaService, collectionCommon, answerActivityService, dataData)
questionController := controller.NewQuestionController(questionService, rankService)
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService)
questionController := controller.NewQuestionController(questionService, answerService, rankService)
dashboardService := dashboard.NewDashboardService(questionRepo, answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configRepo, siteInfoCommonService, serviceConf, dataData)
answerController := controller.NewAnswerController(answerService, rankService, dashboardService)
searchParser := search_parser.NewSearchParser(tagCommonService, userCommon)

View File

@ -3063,6 +3063,45 @@ const docTemplate = `{
}
}
},
"/answer/api/v1/question/answer": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "add question and answer",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Question"
],
"summary": "add question and answer",
"parameters": [
{
"description": "question",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.QuestionAddByAnswer"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/api/v1/question/closemsglist": {
"get": {
"security": [
@ -6751,6 +6790,41 @@ const docTemplate = `{
}
}
},
"schema.QuestionAddByAnswer": {
"type": "object",
"required": [
"answer_content",
"content",
"tags",
"title"
],
"properties": {
"answer_content": {
"type": "string",
"maxLength": 65535,
"minLength": 6
},
"content": {
"description": "content",
"type": "string",
"maxLength": 65535,
"minLength": 6
},
"tags": {
"description": "tags",
"type": "array",
"items": {
"$ref": "#/definitions/schema.TagItem"
}
},
"title": {
"description": "question title",
"type": "string",
"maxLength": 150,
"minLength": 6
}
}
},
"schema.QuestionPageReq": {
"type": "object",
"properties": {

View File

@ -3051,6 +3051,45 @@
}
}
},
"/answer/api/v1/question/answer": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "add question and answer",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Question"
],
"summary": "add question and answer",
"parameters": [
{
"description": "question",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.QuestionAddByAnswer"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/api/v1/question/closemsglist": {
"get": {
"security": [
@ -6739,6 +6778,41 @@
}
}
},
"schema.QuestionAddByAnswer": {
"type": "object",
"required": [
"answer_content",
"content",
"tags",
"title"
],
"properties": {
"answer_content": {
"type": "string",
"maxLength": 65535,
"minLength": 6
},
"content": {
"description": "content",
"type": "string",
"maxLength": 65535,
"minLength": 6
},
"tags": {
"description": "tags",
"type": "array",
"items": {
"$ref": "#/definitions/schema.TagItem"
}
},
"title": {
"description": "question title",
"type": "string",
"maxLength": 150,
"minLength": 6
}
}
},
"schema.QuestionPageReq": {
"type": "object",
"properties": {

View File

@ -1019,6 +1019,33 @@ definitions:
- tags
- title
type: object
schema.QuestionAddByAnswer:
properties:
answer_content:
maxLength: 65535
minLength: 6
type: string
content:
description: content
maxLength: 65535
minLength: 6
type: string
tags:
description: tags
items:
$ref: '#/definitions/schema.TagItem'
type: array
title:
description: question title
maxLength: 150
minLength: 6
type: string
required:
- answer_content
- content
- tags
- title
type: object
schema.QuestionPageReq:
properties:
orderCond:
@ -3796,6 +3823,30 @@ paths:
summary: update question
tags:
- Question
/answer/api/v1/question/answer:
post:
consumes:
- application/json
description: add question and answer
parameters:
- description: question
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.QuestionAddByAnswer'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: add question and answer
tags:
- Question
/answer/api/v1/question/closemsglist:
get:
consumes:

View File

@ -138,7 +138,6 @@ func (ac *AnswerController) Add(ctx *gin.Context) {
return
}
if !has {
// todo !has
handler.HandleResponse(ctx, nil, nil)
return
}

View File

@ -14,18 +14,28 @@ import (
"github.com/answerdev/answer/pkg/converter"
"github.com/answerdev/answer/pkg/uid"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors"
)
// QuestionController question controller
type QuestionController struct {
questionService *service.QuestionService
answerService *service.AnswerService
rankService *rank.RankService
}
// NewQuestionController new controller
func NewQuestionController(questionService *service.QuestionService, rankService *rank.RankService) *QuestionController {
return &QuestionController{questionService: questionService, rankService: rankService}
func NewQuestionController(
questionService *service.QuestionService,
answerService *service.AnswerService,
rankService *rank.RankService,
) *QuestionController {
return &QuestionController{
questionService: questionService,
answerService: answerService,
rankService: rankService,
}
}
// RemoveQuestion delete question
@ -281,6 +291,109 @@ func (qc *QuestionController) AddQuestion(ctx *gin.Context) {
handler.HandleResponse(ctx, err, resp)
}
// 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,
})
if err != nil {
handler.HandleResponse(ctx, err, nil)
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]
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...)
}
}
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)
}
// UpdateQuestion update question
// @Summary update question
// @Description update question

View File

@ -186,6 +186,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
// question
r.POST("/question", a.questionController.AddQuestion)
r.POST("/question/answer", a.questionController.AddQuestionByAnswer)
r.PUT("/question", a.questionController.UpdateQuestion)
r.DELETE("/question", a.questionController.RemoveQuestion)
r.PUT("/question/status", a.questionController.CloseQuestion)

View File

@ -63,6 +63,33 @@ func (req *QuestionAdd) Check() (errFields []*validator.FormErrorField, err erro
return nil, nil
}
type QuestionAddByAnswer struct {
// question title
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
// content
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
// html
HTML string `json:"-"`
AnswerContent string `validate:"required,notblank,gte=6,lte=65535" json:"answer_content"`
AnswerHTML string `json:"-"`
// tags
Tags []*TagItem `validate:"required,dive" json:"tags"`
// user id
UserID string `json:"-"`
QuestionPermission
}
func (req *QuestionAddByAnswer) Check() (errFields []*validator.FormErrorField, err error) {
req.HTML = converter.Markdown2HTML(req.Content)
req.AnswerHTML = converter.Markdown2HTML(req.AnswerContent)
for _, tag := range req.Tags {
if len(tag.OriginalText) > 0 {
tag.ParsedText = converter.Markdown2HTML(tag.OriginalText)
}
}
return nil, nil
}
type QuestionPermission struct {
// whether user can add it
CanAdd bool `json:"-"`

View File

@ -20,7 +20,6 @@ import (
"github.com/answerdev/answer/pkg/encryption"
"github.com/answerdev/answer/pkg/htmltext"
"github.com/answerdev/answer/pkg/uid"
"github.com/davecgh/go-spew/spew"
"github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
@ -448,7 +447,6 @@ func (cs *CommentService) GetCommentPersonalWithPage(ctx context.Context, req *s
if err != nil {
log.Error(err)
} else {
spew.Dump("==", objInfo)
commentResp.ObjectType = objInfo.ObjectType
commentResp.Title = objInfo.Title
commentResp.UrlTitle = htmltext.UrlTitle(objInfo.Title)

View File

@ -335,12 +335,15 @@ func (qs *QuestionCommon) FormatQuestionsPage(
} 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
userInfo, ok := userInfoMap[item.Operator.ID]
if ok {
if userInfo != nil {
item.Operator.DisplayName = userInfo.DisplayName
item.Operator.Username = userInfo.Username
item.Operator.Rank = userInfo.Rank
}
}
}
return formattedQuestions, nil
}

View File

@ -64,6 +64,5 @@ func TestUrlTitle(t *testing.T) {
for _, title := range list {
formatTitle := UrlTitle(title)
spew.Dump(formatTitle)
}
}