mirror of https://gitee.com/answerdev/answer.git
Merge branch 'feat/1.1.0/sql' into feat/1.1.0/report
# Conflicts: # internal/controller/question_controller.go # internal/migrations/migrations.go # internal/migrations/v13.go
This commit is contained in:
commit
01bd603570
|
@ -15,7 +15,7 @@ builds:
|
|||
- id: build
|
||||
main: ./cmd/answer/.
|
||||
binary: answer
|
||||
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
|
||||
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.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 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
|
||||
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.Time={{.Date}} -X main.BuildUser=goreleaser
|
||||
flags: -v
|
||||
goos:
|
||||
- windows
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"eslint.workingDirectories": [
|
||||
"ui"
|
||||
]
|
||||
],
|
||||
"commentTranslate.multiLineMerge": true
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
# Answer - 构建问答社区
|
||||
|
||||
一款问答形式的知识社区开源软件,用来快速构建产品你的产品技术社区、客户支持社区、用户社区等。
|
||||
一款问答形式的知识社区开源软件,你可以使用它快速建立你的问答社区,用于产品技术支持、客户支持、用户交流等。
|
||||
|
||||
了解更多关于该项目的内容,请访问 [answer.dev](https://answer.dev).
|
||||
|
||||
|
|
161
docs/docs.go
161
docs/docs.go
|
@ -3654,6 +3654,81 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/question/invite": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "get question invite user info",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Question"
|
||||
],
|
||||
"summary": "get question invite user info",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"default": "1",
|
||||
"description": "Question ID",
|
||||
"name": "id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "update question invite user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Question"
|
||||
],
|
||||
"summary": "update question invite user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "question",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.QuestionUpdateInviteUser"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/question/operation": {
|
||||
"put": {
|
||||
"security": [
|
||||
|
@ -5103,6 +5178,55 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/info/search": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "SearchUserListByName",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "SearchUserListByName",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/schema.GetOtherUserInfoResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/interface": {
|
||||
"put": {
|
||||
"security": [
|
||||
|
@ -7618,6 +7742,12 @@ const docTemplate = `{
|
|||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"mention_username_list": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"description": "tags",
|
||||
"type": "array",
|
||||
|
@ -7781,6 +7911,12 @@ const docTemplate = `{
|
|||
"description": "question id",
|
||||
"type": "string"
|
||||
},
|
||||
"invite_user": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"description": "tags",
|
||||
"type": "array",
|
||||
|
@ -7796,6 +7932,23 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.QuestionUpdateInviteUser": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"invite_user": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.RemoveAnswerReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -8925,6 +9078,14 @@ const docTemplate = `{
|
|||
"pass"
|
||||
],
|
||||
"properties": {
|
||||
"captcha_code": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
},
|
||||
"captcha_id": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
},
|
||||
"old_pass": {
|
||||
"type": "string",
|
||||
"maxLength": 32,
|
||||
|
|
|
@ -3642,6 +3642,81 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/question/invite": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "get question invite user info",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Question"
|
||||
],
|
||||
"summary": "get question invite user info",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"default": "1",
|
||||
"description": "Question ID",
|
||||
"name": "id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "update question invite user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Question"
|
||||
],
|
||||
"summary": "update question invite user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "question",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.QuestionUpdateInviteUser"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/question/operation": {
|
||||
"put": {
|
||||
"security": [
|
||||
|
@ -5091,6 +5166,55 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/info/search": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "SearchUserListByName",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "SearchUserListByName",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username",
|
||||
"name": "username",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/schema.GetOtherUserInfoResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/interface": {
|
||||
"put": {
|
||||
"security": [
|
||||
|
@ -7606,6 +7730,12 @@
|
|||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"mention_username_list": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"description": "tags",
|
||||
"type": "array",
|
||||
|
@ -7769,6 +7899,12 @@
|
|||
"description": "question id",
|
||||
"type": "string"
|
||||
},
|
||||
"invite_user": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"description": "tags",
|
||||
"type": "array",
|
||||
|
@ -7784,6 +7920,23 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.QuestionUpdateInviteUser": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"invite_user": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.RemoveAnswerReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -8913,6 +9066,14 @@
|
|||
"pass"
|
||||
],
|
||||
"properties": {
|
||||
"captcha_code": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
},
|
||||
"captcha_id": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
},
|
||||
"old_pass": {
|
||||
"type": "string",
|
||||
"maxLength": 32,
|
||||
|
|
|
@ -1218,6 +1218,10 @@ definitions:
|
|||
maxLength: 65535
|
||||
minLength: 6
|
||||
type: string
|
||||
mention_username_list:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
tags:
|
||||
description: tags
|
||||
items:
|
||||
|
@ -1334,6 +1338,10 @@ definitions:
|
|||
id:
|
||||
description: question id
|
||||
type: string
|
||||
invite_user:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
tags:
|
||||
description: tags
|
||||
items:
|
||||
|
@ -1350,6 +1358,17 @@ definitions:
|
|||
- tags
|
||||
- title
|
||||
type: object
|
||||
schema.QuestionUpdateInviteUser:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
invite_user:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- id
|
||||
type: object
|
||||
schema.RemoveAnswerReq:
|
||||
properties:
|
||||
id:
|
||||
|
@ -2134,6 +2153,12 @@ definitions:
|
|||
type: object
|
||||
schema.UserModifyPasswordReq:
|
||||
properties:
|
||||
captcha_code:
|
||||
maxLength: 500
|
||||
type: string
|
||||
captcha_id:
|
||||
maxLength: 500
|
||||
type: string
|
||||
old_pass:
|
||||
maxLength: 32
|
||||
minLength: 8
|
||||
|
@ -4467,6 +4492,53 @@ paths:
|
|||
summary: get question details
|
||||
tags:
|
||||
- Question
|
||||
/answer/api/v1/question/invite:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: get question invite user info
|
||||
parameters:
|
||||
- default: "1"
|
||||
description: Question ID
|
||||
in: query
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: get question invite user info
|
||||
tags:
|
||||
- Question
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: update question invite user
|
||||
parameters:
|
||||
- description: question
|
||||
in: body
|
||||
name: data
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/schema.QuestionUpdateInviteUser'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.RespBody'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: update question invite user
|
||||
tags:
|
||||
- Question
|
||||
/answer/api/v1/question/operation:
|
||||
put:
|
||||
consumes:
|
||||
|
@ -5350,6 +5422,34 @@ paths:
|
|||
summary: UserUpdateInfo update user info
|
||||
tags:
|
||||
- User
|
||||
/answer/api/v1/user/info/search:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: SearchUserListByName
|
||||
parameters:
|
||||
- description: username
|
||||
in: query
|
||||
name: username
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/handler.RespBody'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/schema.GetOtherUserInfoResp'
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: SearchUserListByName
|
||||
tags:
|
||||
- User
|
||||
/answer/api/v1/user/interface:
|
||||
put:
|
||||
consumes:
|
||||
|
|
|
@ -35,4 +35,6 @@ const (
|
|||
NotificationYourAnswerWasDeleted = "notification.action.your_answer_was_deleted"
|
||||
// NotificationYourCommentWasDeleted your comment was deleted
|
||||
NotificationYourCommentWasDeleted = "notification.action.your_comment_was_deleted"
|
||||
// NotificationInvitedYouToAnswer invited you to answer
|
||||
NotificationInvitedYouToAnswer = "notification.action.invited_you_to_answer"
|
||||
)
|
||||
|
|
|
@ -221,6 +221,44 @@ func (qc *QuestionController) GetQuestion(ctx *gin.Context) {
|
|||
handler.HandleResponse(ctx, nil, info)
|
||||
}
|
||||
|
||||
// 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 ""
|
||||
// @Router /answer/api/v1/question/invite [get]
|
||||
func (qc *QuestionController) GetQuestionInviteUserInfo(ctx *gin.Context) {
|
||||
id := ctx.Query("id")
|
||||
id = uid.DeShortID(id)
|
||||
userID := middleware.GetLoginUserIDFromContext(ctx)
|
||||
req := schema.QuestionPermission{}
|
||||
canList, err := qc.rankService.CheckOperationPermissions(ctx, userID, []string{
|
||||
permission.QuestionEdit,
|
||||
})
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
objectOwner := qc.rankService.CheckOperationObjectOwner(ctx, userID, id)
|
||||
|
||||
req.CanEdit = canList[0] || objectOwner
|
||||
if !req.CanEdit {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
list, err := qc.questionService.InviteUserInfo(ctx, id)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
handler.HandleResponse(ctx, nil, list)
|
||||
|
||||
}
|
||||
|
||||
// SimilarQuestion godoc
|
||||
// @Summary Search Similar Question
|
||||
// @Description Search Similar Question
|
||||
|
@ -500,6 +538,51 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
|
|||
handler.HandleResponse(ctx, nil, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview})
|
||||
}
|
||||
|
||||
// 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
|
||||
// @Router /answer/api/v1/question/invite [put]
|
||||
func (qc *QuestionController) UpdateQuestionInviteUser(ctx *gin.Context) {
|
||||
req := &schema.QuestionUpdateInviteUser{}
|
||||
errFields := handler.BindAndCheckReturnErr(ctx, req)
|
||||
if ctx.IsAborted() {
|
||||
return
|
||||
}
|
||||
req.ID = uid.DeShortID(req.ID)
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
permission.QuestionEdit,
|
||||
})
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
|
||||
objectOwner := qc.rankService.CheckOperationObjectOwner(ctx, req.UserID, req.ID)
|
||||
req.CanEdit = canList[0] || objectOwner
|
||||
if !req.CanEdit {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
if len(errFields) > 0 {
|
||||
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
|
||||
return
|
||||
}
|
||||
err = qc.questionService.UpdateQuestionInviteUser(ctx, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
handler.HandleResponse(ctx, nil, nil)
|
||||
}
|
||||
|
||||
// SearchByTitleLike add question title like
|
||||
// @Summary add question title like
|
||||
// @Description add question title like
|
||||
|
|
|
@ -470,6 +470,8 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI
|
|||
data["description"] = siteInfo.Description
|
||||
data["language"] = handler.GetLang(ctx)
|
||||
data["timezone"] = siteInfo.Interface.TimeZone
|
||||
language := strings.Replace(siteInfo.Interface.Language, "_", "-", -1)
|
||||
data["lang"] = language
|
||||
data["HeadCode"] = siteInfo.CustomCssHtml.CustomHead
|
||||
data["HeaderCode"] = siteInfo.CustomCssHtml.CustomHeader
|
||||
data["FooterCode"] = siteInfo.CustomCssHtml.CustomFooter
|
||||
|
|
|
@ -350,6 +350,21 @@ func (uc *UserController) UserModifyPassWord(ctx *gin.Context) {
|
|||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
req.AccessToken = middleware.ExtractToken(ctx)
|
||||
|
||||
captchaPass := uc.actionService.ActionRecordVerifyCaptcha(ctx, schema.ActionRecordTypeModifyPass, ctx.ClientIP(),
|
||||
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
|
||||
}
|
||||
_, err := uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeModifyPass, ctx.ClientIP())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
oldPassVerification, err := uc.userService.UserModifyPassWordVerification(ctx, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
|
@ -363,6 +378,7 @@ func (uc *UserController) UserModifyPassWord(ctx *gin.Context) {
|
|||
handler.HandleResponse(ctx, errors.BadRequest(reason.OldPasswordVerificationFailed), errFields)
|
||||
return
|
||||
}
|
||||
|
||||
if req.OldPass == req.Pass {
|
||||
errFields := append([]*validator.FormErrorField{}, &validator.FormErrorField{
|
||||
ErrorField: "pass",
|
||||
|
@ -372,6 +388,9 @@ func (uc *UserController) UserModifyPassWord(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
err = uc.userService.UserModifyPassword(ctx, req)
|
||||
if err == nil {
|
||||
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeLogin, ctx.ClientIP())
|
||||
}
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
@ -588,3 +607,22 @@ func (uc *UserController) UserUnsubscribeEmailNotification(ctx *gin.Context) {
|
|||
err := uc.userService.UserUnsubscribeEmailNotification(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
// SearchUserListByName godoc
|
||||
// @Summary SearchUserListByName
|
||||
// @Description SearchUserListByName
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param username query string true "username"
|
||||
// @Success 200 {object} handler.RespBody{data=schema.GetOtherUserInfoResp}
|
||||
// @Router /answer/api/v1/user/info/search [get]
|
||||
func (uc *UserController) SearchUserListByName(ctx *gin.Context) {
|
||||
req := &schema.GetOtherUserInfoByUsernameReq{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
resp, err := uc.userService.SearchUserListByName(ctx, req.Username)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ type Question struct {
|
|||
CreatedAt time.Time `xorm:"not null default CURRENT_TIMESTAMP TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"updated_at TIMESTAMP"`
|
||||
UserID string `xorm:"not null default 0 BIGINT(20) INDEX user_id"`
|
||||
InviteUserID string `xorm:"TEXT invite_user_id"`
|
||||
LastEditUserID string `xorm:"not null default 0 BIGINT(20) last_edit_user_id"`
|
||||
Title string `xorm:"not null default '' VARCHAR(150) title"`
|
||||
OriginalText string `xorm:"not null MEDIUMTEXT original_text"`
|
||||
|
|
|
@ -4,6 +4,7 @@ import "time"
|
|||
|
||||
const (
|
||||
TagRelStatusAvailable = 1
|
||||
TagRelStatusHide = 2
|
||||
TagRelStatusDeleted = 10
|
||||
)
|
||||
|
||||
|
|
|
@ -3,17 +3,24 @@ package migrations
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/permission"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func updateCount(x *xorm.Engine) error {
|
||||
addPrivilegeForInviteSomeoneToAnswer(x)
|
||||
addGravatarBaseURL(x)
|
||||
updateQuestionCount(x)
|
||||
updateTagCount(x)
|
||||
updateUserQuestionCount(x)
|
||||
updateUserAnswerCount(x)
|
||||
inviteAnswer(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -97,3 +104,236 @@ func addPrivilegeForInviteSomeoneToAnswer(x *xorm.Engine) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateQuestionCount(x *xorm.Engine) error {
|
||||
//question answer count
|
||||
answers := make([]entity.Answer, 0)
|
||||
err := x.Find(&answers, &entity.Answer{Status: entity.AnswerStatusAvailable})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get answers failed: %w", err)
|
||||
}
|
||||
questionAnswerCount := make(map[string]int)
|
||||
for _, answer := range answers {
|
||||
_, ok := questionAnswerCount[answer.QuestionID]
|
||||
if !ok {
|
||||
questionAnswerCount[answer.QuestionID] = 1
|
||||
} else {
|
||||
questionAnswerCount[answer.QuestionID]++
|
||||
}
|
||||
}
|
||||
questionList := make([]entity.Question, 0)
|
||||
err = x.Find(&questionList, &entity.Question{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get questions failed: %w", err)
|
||||
}
|
||||
for _, item := range questionList {
|
||||
_, ok := questionAnswerCount[item.ID]
|
||||
if ok {
|
||||
item.AnswerCount = questionAnswerCount[item.ID]
|
||||
if _, err = x.Update(item, &entity.Question{ID: item.ID}); err != nil {
|
||||
log.Errorf("update %+v config failed: %s", item, err)
|
||||
return fmt.Errorf("update question failed: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateTagCount update tag count
|
||||
func updateTagCount(x *xorm.Engine) error {
|
||||
tagRelList := make([]entity.TagRel, 0)
|
||||
err := x.Find(&tagRelList, &entity.TagRel{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get tag rel failed: %w", err)
|
||||
}
|
||||
questionIDs := make([]string, 0)
|
||||
questionsAvailableMap := make(map[string]bool)
|
||||
questionsHideMap := make(map[string]bool)
|
||||
for _, item := range tagRelList {
|
||||
questionIDs = append(questionIDs, item.ObjectID)
|
||||
questionsAvailableMap[item.ObjectID] = false
|
||||
questionsHideMap[item.ObjectID] = false
|
||||
}
|
||||
questionList := make([]entity.Question, 0)
|
||||
err = x.In("id", questionIDs).In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusClosed}).Find(&questionList, &entity.Question{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get questions failed: %w", err)
|
||||
}
|
||||
for _, question := range questionList {
|
||||
_, ok := questionsAvailableMap[question.ID]
|
||||
if ok {
|
||||
questionsAvailableMap[question.ID] = true
|
||||
if question.Show == entity.QuestionHide {
|
||||
questionsHideMap[question.ID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for id, ok := range questionsHideMap {
|
||||
if ok {
|
||||
if _, err = x.Cols("status").Update(&entity.TagRel{Status: entity.TagRelStatusHide}, &entity.TagRel{ObjectID: id}); err != nil {
|
||||
log.Errorf("update %+v config failed: %s", id, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for id, ok := range questionsAvailableMap {
|
||||
if !ok {
|
||||
if _, err = x.Cols("status").Update(&entity.TagRel{Status: entity.TagRelStatusDeleted}, &entity.TagRel{ObjectID: id}); err != nil {
|
||||
log.Errorf("update %+v config failed: %s", id, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//select tag count
|
||||
newTagRelList := make([]entity.TagRel, 0)
|
||||
err = x.Find(&newTagRelList, &entity.TagRel{Status: entity.TagRelStatusAvailable})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get tag rel failed: %w", err)
|
||||
}
|
||||
tagCountMap := make(map[string]int)
|
||||
for _, v := range newTagRelList {
|
||||
_, ok := tagCountMap[v.TagID]
|
||||
if !ok {
|
||||
tagCountMap[v.TagID] = 1
|
||||
} else {
|
||||
tagCountMap[v.TagID]++
|
||||
}
|
||||
}
|
||||
TagList := make([]entity.Tag, 0)
|
||||
err = x.Find(&TagList, &entity.Tag{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get tag failed: %w", err)
|
||||
}
|
||||
for _, tag := range TagList {
|
||||
_, ok := tagCountMap[tag.ID]
|
||||
if ok {
|
||||
tag.QuestionCount = tagCountMap[tag.ID]
|
||||
if _, err = x.Update(tag, &entity.Tag{ID: tag.ID}); err != nil {
|
||||
log.Errorf("update %+v tag failed: %s", tag.ID, err)
|
||||
return fmt.Errorf("update tag failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
tag.QuestionCount = 0
|
||||
if _, err = x.Update(tag, &entity.Tag{ID: tag.ID}); err != nil {
|
||||
log.Errorf("update %+v tag failed: %s", tag.ID, err)
|
||||
return fmt.Errorf("update tag failed: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateUserQuestionCount update user question count
|
||||
func updateUserQuestionCount(x *xorm.Engine) error {
|
||||
questionList := make([]entity.Question, 0)
|
||||
err := x.In("status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusClosed}).Find(&questionList, &entity.Question{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get question failed: %w", err)
|
||||
}
|
||||
userQuestionCountMap := make(map[string]int)
|
||||
for _, question := range questionList {
|
||||
_, ok := userQuestionCountMap[question.UserID]
|
||||
if !ok {
|
||||
userQuestionCountMap[question.UserID] = 1
|
||||
} else {
|
||||
userQuestionCountMap[question.UserID]++
|
||||
}
|
||||
}
|
||||
userList := make([]entity.User, 0)
|
||||
err = x.Find(&userList, &entity.User{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get user failed: %w", err)
|
||||
}
|
||||
for _, user := range userList {
|
||||
_, ok := userQuestionCountMap[user.ID]
|
||||
if ok {
|
||||
user.QuestionCount = userQuestionCountMap[user.ID]
|
||||
if _, err = x.Cols("question_count").Update(user, &entity.User{ID: user.ID}); err != nil {
|
||||
log.Errorf("update %+v user failed: %s", user.ID, err)
|
||||
return fmt.Errorf("update user failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
user.QuestionCount = 0
|
||||
if _, err = x.Cols("question_count").Update(user, &entity.User{ID: user.ID}); err != nil {
|
||||
log.Errorf("update %+v user failed: %s", user.ID, err)
|
||||
return fmt.Errorf("update user failed: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateUserAnswerCount update user answer count
|
||||
func updateUserAnswerCount(x *xorm.Engine) error {
|
||||
answers := make([]entity.Answer, 0)
|
||||
err := x.Find(&answers, &entity.Answer{Status: entity.AnswerStatusAvailable})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get answers failed: %w", err)
|
||||
}
|
||||
userAnswerCount := make(map[string]int)
|
||||
for _, answer := range answers {
|
||||
_, ok := userAnswerCount[answer.UserID]
|
||||
if !ok {
|
||||
userAnswerCount[answer.UserID] = 1
|
||||
} else {
|
||||
userAnswerCount[answer.UserID]++
|
||||
}
|
||||
}
|
||||
userList := make([]entity.User, 0)
|
||||
err = x.Find(&userList, &entity.User{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get user failed: %w", err)
|
||||
}
|
||||
for _, user := range userList {
|
||||
_, ok := userAnswerCount[user.ID]
|
||||
if ok {
|
||||
user.AnswerCount = userAnswerCount[user.ID]
|
||||
if _, err = x.Cols("answer_count").Update(user, &entity.User{ID: user.ID}); err != nil {
|
||||
log.Errorf("update %+v user failed: %s", user.ID, err)
|
||||
return fmt.Errorf("update user failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
user.AnswerCount = 0
|
||||
if _, err = x.Cols("answer_count").Update(user, &entity.User{ID: user.ID}); err != nil {
|
||||
log.Errorf("update %+v user failed: %s", user.ID, err)
|
||||
return fmt.Errorf("update user failed: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func inviteAnswer(x *xorm.Engine) error {
|
||||
type Question struct {
|
||||
ID string `xorm:"not null pk BIGINT(20) id"`
|
||||
CreatedAt time.Time `xorm:"not null default CURRENT_TIMESTAMP TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"updated_at TIMESTAMP"`
|
||||
UserID string `xorm:"not null default 0 BIGINT(20) INDEX user_id"`
|
||||
InviteUserID string `xorm:"TEXT invite_user_id"`
|
||||
LastEditUserID string `xorm:"not null default 0 BIGINT(20) last_edit_user_id"`
|
||||
Title string `xorm:"not null default '' VARCHAR(150) title"`
|
||||
OriginalText string `xorm:"not null MEDIUMTEXT original_text"`
|
||||
ParsedText string `xorm:"not null MEDIUMTEXT parsed_text"`
|
||||
Status int `xorm:"not null default 1 INT(11) status"`
|
||||
Pin int `xorm:"not null default 1 INT(11) pin"`
|
||||
Show int `xorm:"not null default 1 INT(11) show"`
|
||||
ViewCount int `xorm:"not null default 0 INT(11) view_count"`
|
||||
UniqueViewCount int `xorm:"not null default 0 INT(11) unique_view_count"`
|
||||
VoteCount int `xorm:"not null default 0 INT(11) vote_count"`
|
||||
AnswerCount int `xorm:"not null default 0 INT(11) answer_count"`
|
||||
CollectionCount int `xorm:"not null default 0 INT(11) collection_count"`
|
||||
FollowCount int `xorm:"not null default 0 INT(11) follow_count"`
|
||||
AcceptedAnswerID string `xorm:"not null default 0 BIGINT(20) accepted_answer_id"`
|
||||
LastAnswerID string `xorm:"not null default 0 BIGINT(20) last_answer_id"`
|
||||
PostUpdateTime time.Time `xorm:"post_update_time TIMESTAMP"`
|
||||
RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
}
|
||||
err := x.Sync(new(Question))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -219,6 +219,24 @@ func (qr *questionRepo) GetQuestionCount(ctx context.Context) (count int64, err
|
|||
return
|
||||
}
|
||||
|
||||
func (qr *questionRepo) GetUserQuestionCount(ctx context.Context, userID string) (count int64, err error) {
|
||||
questionList := make([]*entity.Question, 0)
|
||||
count, err = qr.data.DB.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusClosed}).And("user_id = ?", userID).Count(&questionList)
|
||||
if err != nil {
|
||||
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (qr *questionRepo) GetQuestionCountByIDs(ctx context.Context, ids []string) (count int64, err error) {
|
||||
questionList := make([]*entity.Question, 0)
|
||||
count, err = qr.data.DB.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusClosed}).In("id = ?", ids).Count(&questionList)
|
||||
if err != nil {
|
||||
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (qr *questionRepo) GetQuestionIDsPage(ctx context.Context, page, pageSize int) (questionIDList []*schema.SiteMapQuestionInfo, err error) {
|
||||
questionIDList = make([]*schema.SiteMapQuestionInfo, 0)
|
||||
rows := make([]*entity.Question, 0)
|
||||
|
|
|
@ -138,7 +138,7 @@ func (ur *UserRankRepo) UserRankPage(ctx context.Context, userID string, page, p
|
|||
) {
|
||||
rankPage = make([]*entity.Activity, 0)
|
||||
|
||||
session := ur.data.DB.Where(builder.Eq{"has_rank": 1}.And(builder.Eq{"cancelled": 0}))
|
||||
session := ur.data.DB.Where(builder.Eq{"has_rank": 1}.And(builder.Eq{"cancelled": 0})).And(builder.Gt{"rank": 0})
|
||||
session.Desc("created_at")
|
||||
|
||||
cond := &entity.Activity{UserID: userID}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
tagcommon "github.com/answerdev/answer/internal/service/tag_common"
|
||||
"github.com/answerdev/answer/internal/service/unique"
|
||||
"github.com/answerdev/answer/pkg/uid"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
|
@ -52,6 +53,26 @@ func (tr *tagRelRepo) RemoveTagRelListByObjectID(ctx context.Context, objectID s
|
|||
return
|
||||
}
|
||||
|
||||
func (tr *tagRelRepo) HideTagRelListByObjectID(ctx context.Context, objectID string) (err error) {
|
||||
spew.Dump("====== HideTagRelListByObjectID")
|
||||
objectID = uid.DeShortID(objectID)
|
||||
_, err = tr.data.DB.Where("object_id = ?", objectID).Cols("status").Update(&entity.TagRel{Status: entity.TagRelStatusHide})
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tr *tagRelRepo) ShowTagRelListByObjectID(ctx context.Context, objectID string) (err error) {
|
||||
spew.Dump("====== ShowTagRelListByObjectID")
|
||||
objectID = uid.DeShortID(objectID)
|
||||
_, err = tr.data.DB.Where("object_id = ?", objectID).Cols("status").Update(&entity.TagRel{Status: entity.TagRelStatusAvailable})
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveTagRelListByIDs delete tag list
|
||||
func (tr *tagRelRepo) RemoveTagRelListByIDs(ctx context.Context, ids []int64) (err error) {
|
||||
_, err = tr.data.DB.In("id", ids).Update(&entity.TagRel{Status: entity.TagRelStatusDeleted})
|
||||
|
@ -90,7 +111,7 @@ func (tr *tagRelRepo) GetObjectTagRelList(ctx context.Context, objectID string)
|
|||
objectID = uid.DeShortID(objectID)
|
||||
tagListList = make([]*entity.TagRel, 0)
|
||||
session := tr.data.DB.Where("object_id = ?", objectID)
|
||||
session.Where("status = ?", entity.TagRelStatusAvailable)
|
||||
session.In("status", []int{entity.TagRelStatusAvailable, entity.TagRelStatusHide})
|
||||
err = session.Find(&tagListList)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
|
|
|
@ -71,6 +71,26 @@ func (ur *userRepo) IncreaseQuestionCount(ctx context.Context, userID string, am
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ur *userRepo) UpdateQuestionCount(ctx context.Context, userID string, count int64) (err error) {
|
||||
user := &entity.User{}
|
||||
user.QuestionCount = int(count)
|
||||
_, err = ur.data.DB.Where("id = ?", userID).Cols("question_count").Update(user)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ur *userRepo) UpdateAnswerCount(ctx context.Context, userID string, count int) (err error) {
|
||||
user := &entity.User{}
|
||||
user.AnswerCount = count
|
||||
_, err = ur.data.DB.Where("id = ?", userID).Cols("answer_count").Update(user)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateLastLoginDate update last login date
|
||||
func (ur *userRepo) UpdateLastLoginDate(ctx context.Context, userID string) (err error) {
|
||||
user := &entity.User{LastLoginDate: time.Now()}
|
||||
|
@ -175,6 +195,17 @@ func (ur *userRepo) GetByUsername(ctx context.Context, username string) (userInf
|
|||
return
|
||||
}
|
||||
|
||||
func (ur *userRepo) GetByUsernames(ctx context.Context, usernames []string) ([]*entity.User, error) {
|
||||
list := make([]*entity.User, 0)
|
||||
err := ur.data.DB.Where("status =?", entity.UserStatusAvailable).In("username", usernames).Find(&list)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
return list, err
|
||||
}
|
||||
tryToDecorateUserListFromUserCenter(ctx, ur.data, list)
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// GetByEmail get user by email
|
||||
func (ur *userRepo) GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error) {
|
||||
userInfo = &entity.User{}
|
||||
|
@ -195,6 +226,23 @@ func (ur *userRepo) GetUserCount(ctx context.Context) (count int64, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (ur *userRepo) SearchUserListByName(ctx context.Context, name string) (userList []*entity.User, err error) {
|
||||
userList = make([]*entity.User, 0)
|
||||
if name == "" {
|
||||
return userList, nil
|
||||
}
|
||||
session := ur.data.DB.Where("")
|
||||
session.Where("username LIKE LOWER(?) or display_name LIKE ?", name+"%", name+"%").And("status =?", entity.UserStatusAvailable)
|
||||
session.Asc("username")
|
||||
session = session.Limit(5, 0)
|
||||
err = session.OrderBy("id desc").Find(&userList)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
tryToDecorateUserListFromUserCenter(ctx, ur.data, userList)
|
||||
return
|
||||
}
|
||||
|
||||
func tryToDecorateUserInfoFromUserCenter(ctx context.Context, data *data.Data, original *entity.User) (err error) {
|
||||
if original == nil {
|
||||
return nil
|
||||
|
|
|
@ -112,6 +112,7 @@ func (a *AnswerAPIRouter) RegisterMustUnAuthAnswerAPIRouter(r *gin.RouterGroup)
|
|||
routerGroup.POST("/user/password/reset", a.userController.RetrievePassWord)
|
||||
routerGroup.POST("/user/password/replacement", a.userController.UseRePassWord)
|
||||
routerGroup.PUT("/user/email/notification", a.userController.UserUnsubscribeEmailNotification)
|
||||
routerGroup.GET("/user/info/search", a.userController.SearchUserListByName)
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
|
||||
|
@ -129,6 +130,7 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
|
|||
|
||||
//question
|
||||
r.GET("/question/info", a.questionController.GetQuestion)
|
||||
r.GET("/question/invite", a.questionController.GetQuestionInviteUserInfo)
|
||||
r.GET("/question/page", a.questionController.QuestionPage)
|
||||
r.GET("/question/similar/tag", a.questionController.SimilarQuestion)
|
||||
r.GET("/personal/qa/top", a.questionController.UserTop)
|
||||
|
@ -193,6 +195,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
|
|||
r.POST("/question", a.questionController.AddQuestion)
|
||||
r.POST("/question/answer", a.questionController.AddQuestionByAnswer)
|
||||
r.PUT("/question", a.questionController.UpdateQuestion)
|
||||
r.PUT("/question/invite", a.questionController.UpdateQuestionInviteUser)
|
||||
r.DELETE("/question", a.questionController.RemoveQuestion)
|
||||
r.PUT("/question/status", a.questionController.CloseQuestion)
|
||||
r.PUT("/question/operation", a.questionController.OperationQuestion)
|
||||
|
|
|
@ -90,6 +90,9 @@ func (a *UIRouter) Register(r *gin.Engine) {
|
|||
if branding.Favicon != "" {
|
||||
c.String(http.StatusOK, htmltext.GetPicByUrl(branding.Favicon))
|
||||
return
|
||||
} else if branding.SquareIcon != "" {
|
||||
c.String(http.StatusOK, htmltext.GetPicByUrl(branding.SquareIcon))
|
||||
return
|
||||
} else {
|
||||
c.Header("content-type", "image/vnd.microsoft.icon")
|
||||
filePath = UIRootFilePath + urlPath
|
||||
|
|
|
@ -87,7 +87,8 @@ type QuestionAddByAnswer struct {
|
|||
// tags
|
||||
Tags []*TagItem `validate:"required,dive" json:"tags"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
MentionUsernameList []string `validate:"omitempty" json:"mention_username_list"`
|
||||
QuestionPermission
|
||||
}
|
||||
|
||||
|
@ -139,7 +140,8 @@ type QuestionUpdate struct {
|
|||
// content
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
|
||||
// html
|
||||
HTML string `json:"-"`
|
||||
HTML string `json:"-"`
|
||||
InviteUser []string `validate:"omitempty" json:"invite_user"`
|
||||
// tags
|
||||
Tags []*TagItem `validate:"required,dive" json:"tags"`
|
||||
// edit summary
|
||||
|
@ -150,6 +152,13 @@ type QuestionUpdate struct {
|
|||
QuestionPermission
|
||||
}
|
||||
|
||||
type QuestionUpdateInviteUser struct {
|
||||
ID string `validate:"required" json:"id"`
|
||||
InviteUser []string `validate:"omitempty" json:"invite_user"`
|
||||
UserID string `json:"-"`
|
||||
QuestionPermission
|
||||
}
|
||||
|
||||
func (req *QuestionUpdate) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.HTML = converter.Markdown2HTML(req.Content)
|
||||
return nil, nil
|
||||
|
|
|
@ -5,6 +5,7 @@ type SimpleObjectInfo struct {
|
|||
ObjectID string `json:"object_id"`
|
||||
ObjectCreatorUserID string `json:"object_creator_user_id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
QuestionStatus int `json:"status"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
CommentID string `json:"comment_id"`
|
||||
TagID string `json:"tag_id"`
|
||||
|
|
|
@ -222,9 +222,10 @@ const (
|
|||
NoticeStatusOn = 1
|
||||
NoticeStatusOff = 2
|
||||
|
||||
ActionRecordTypeLogin = "login"
|
||||
ActionRecordTypeEmail = "e_mail"
|
||||
ActionRecordTypeFindPass = "find_pass"
|
||||
ActionRecordTypeLogin = "login"
|
||||
ActionRecordTypeEmail = "e_mail"
|
||||
ActionRecordTypeFindPass = "find_pass"
|
||||
ActionRecordTypeModifyPass = "modify_pass"
|
||||
)
|
||||
|
||||
var UserStatusShow = map[int]string{
|
||||
|
@ -277,6 +278,8 @@ type UserModifyPasswordReq struct {
|
|||
Pass string `validate:"required,gte=8,lte=32" json:"pass"`
|
||||
UserID string `json:"-"`
|
||||
AccessToken string `json:"-"`
|
||||
CaptchaID string `validate:"omitempty,gt=0,lte=500" json:"captcha_id"`
|
||||
CaptchaCode string `validate:"omitempty,gt=0,lte=500" json:"captcha_code"`
|
||||
}
|
||||
|
||||
func (u *UserModifyPasswordReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
|
@ -367,7 +370,7 @@ type UserNoticeSetResp struct {
|
|||
|
||||
type ActionRecordReq struct {
|
||||
// action
|
||||
Action string `validate:"required,oneof=login e_mail find_pass" form:"action"`
|
||||
Action string `validate:"required,oneof=login e_mail find_pass modify_pass" form:"action"`
|
||||
IP string `json:"-"`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
package activity_type
|
||||
|
||||
import "github.com/answerdev/answer/internal/repo/config"
|
||||
import (
|
||||
"github.com/answerdev/answer/internal/repo/config"
|
||||
)
|
||||
|
||||
const (
|
||||
QuestionVoteUp = "question.vote_up"
|
||||
QuestionVoteDown = "question.vote_down"
|
||||
AnswerVoteUp = "answer.vote_up"
|
||||
AnswerVoteDown = "answer.vote_down"
|
||||
CommentVoteUp = "comment.vote_up"
|
||||
CommentVoteDown = "comment.vote_down"
|
||||
QuestionVoteUp = "question.vote_up"
|
||||
QuestionVoteDown = "question.vote_down"
|
||||
AnswerVoteUp = "answer.vote_up"
|
||||
AnswerVoteDown = "answer.vote_down"
|
||||
CommentVoteUp = "comment.vote_up"
|
||||
CommentVoteDown = "comment.vote_down"
|
||||
AnswerAccepted = "answer.accepted"
|
||||
AnswerAccept = "answer.accept"
|
||||
QuestionVotedUp = "question.voted_up"
|
||||
QuestionVotedDown = "question.voted_down"
|
||||
AnswerVotedUp = "answer.voted_up"
|
||||
AnswerVotedDown = "answer.voted_down"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -19,14 +27,26 @@ var (
|
|||
AnswerVoteDown,
|
||||
CommentVoteUp,
|
||||
CommentVoteDown,
|
||||
AnswerAccepted,
|
||||
AnswerAccept,
|
||||
QuestionVotedUp,
|
||||
QuestionVotedDown,
|
||||
AnswerVotedUp,
|
||||
AnswerVotedDown,
|
||||
}
|
||||
activityTypeFlagMapping = map[string]string{
|
||||
QuestionVoteUp: "upvote",
|
||||
QuestionVoteDown: "downvote",
|
||||
AnswerVoteUp: "upvote",
|
||||
AnswerVoteDown: "downvote",
|
||||
CommentVoteUp: "upvote",
|
||||
CommentVoteDown: "downvote",
|
||||
QuestionVoteUp: "upvote",
|
||||
QuestionVoteDown: "downvote",
|
||||
AnswerVoteUp: "upvote",
|
||||
AnswerVoteDown: "downvote",
|
||||
CommentVoteUp: "upvote",
|
||||
CommentVoteDown: "downvote",
|
||||
AnswerAccepted: "accepted",
|
||||
AnswerAccept: "accept",
|
||||
QuestionVotedUp: "upvoted",
|
||||
QuestionVotedDown: "downvoted",
|
||||
AnswerVotedUp: "upvoted",
|
||||
AnswerVotedDown: "downvoted",
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -453,6 +453,9 @@ func (cs *CommentService) GetCommentPersonalWithPage(ctx context.Context, req *s
|
|||
commentResp.UrlTitle = htmltext.UrlTitle(objInfo.Title)
|
||||
commentResp.QuestionID = objInfo.QuestionID
|
||||
commentResp.AnswerID = objInfo.AnswerID
|
||||
if objInfo.QuestionStatus == entity.QuestionStatusDeleted {
|
||||
commentResp.Title = "Deleted question"
|
||||
}
|
||||
}
|
||||
}
|
||||
resp = append(resp, commentResp)
|
||||
|
|
|
@ -135,6 +135,7 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
|
|||
ObjectID: questionInfo.ID,
|
||||
ObjectCreatorUserID: questionInfo.UserID,
|
||||
QuestionID: questionInfo.ID,
|
||||
QuestionStatus: questionInfo.Status,
|
||||
ObjectType: objectType,
|
||||
Title: questionInfo.Title,
|
||||
Content: questionInfo.ParsedText, // todo trim
|
||||
|
@ -158,6 +159,7 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
|
|||
ObjectID: answerInfo.ID,
|
||||
ObjectCreatorUserID: answerInfo.UserID,
|
||||
QuestionID: answerInfo.QuestionID,
|
||||
QuestionStatus: questionInfo.Status,
|
||||
AnswerID: answerInfo.ID,
|
||||
ObjectType: objectType,
|
||||
Title: questionInfo.Title, // this should be question title
|
||||
|
@ -185,6 +187,7 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
|
|||
}
|
||||
if exist {
|
||||
objInfo.QuestionID = questionInfo.ID
|
||||
objInfo.QuestionStatus = questionInfo.Status
|
||||
objInfo.Title = questionInfo.Title
|
||||
}
|
||||
answerInfo, exist, err := os.answerRepo.GetAnswer(ctx, commentInfo.ObjectID)
|
||||
|
|
|
@ -46,6 +46,8 @@ type QuestionRepo interface {
|
|||
FindByID(ctx context.Context, id []string) (questionList []*entity.Question, err error)
|
||||
AdminSearchList(ctx context.Context, search *schema.AdminQuestionSearch) ([]*entity.Question, int64, error)
|
||||
GetQuestionCount(ctx context.Context) (count int64, err error)
|
||||
GetUserQuestionCount(ctx context.Context, userID string) (count int64, err error)
|
||||
GetQuestionCountByIDs(ctx context.Context, ids []string) (count int64, err error)
|
||||
GetQuestionIDsPage(ctx context.Context, page, pageSize int) (questionIDList []*schema.SiteMapQuestionInfo, err error)
|
||||
}
|
||||
|
||||
|
@ -88,6 +90,10 @@ func NewQuestionCommon(questionRepo QuestionRepo,
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *QuestionCommon) GetUserQuestionCount(ctx context.Context, userID string) (count int64, err error) {
|
||||
return qs.questionRepo.GetUserQuestionCount(ctx, userID)
|
||||
}
|
||||
|
||||
func (qs *QuestionCommon) UpdatePv(ctx context.Context, questionID string) error {
|
||||
return qs.questionRepo.UpdatePvCount(ctx, questionID)
|
||||
}
|
||||
|
@ -144,6 +150,34 @@ func (qs *QuestionCommon) FindInfoByID(ctx context.Context, questionIDs []string
|
|||
return list, nil
|
||||
}
|
||||
|
||||
func (qs *QuestionCommon) InviteUserInfo(ctx context.Context, questionID string) (inviteList []*schema.UserBasicInfo, err error) {
|
||||
InviteUserInfo := make([]*schema.UserBasicInfo, 0)
|
||||
dbinfo, has, err := qs.questionRepo.GetQuestion(ctx, questionID)
|
||||
if err != nil {
|
||||
return InviteUserInfo, err
|
||||
}
|
||||
if !has {
|
||||
return InviteUserInfo, errors.NotFound(reason.QuestionNotFound)
|
||||
}
|
||||
//InviteUser
|
||||
if dbinfo.InviteUserID != "" {
|
||||
InviteUserIDs := make([]string, 0)
|
||||
err := json.Unmarshal([]byte(dbinfo.InviteUserID), &InviteUserIDs)
|
||||
if err == nil {
|
||||
inviteUserInfoMap, err := qs.userCommon.BatchUserBasicInfoByID(ctx, InviteUserIDs)
|
||||
if err == nil {
|
||||
for _, userid := range InviteUserIDs {
|
||||
_, ok := inviteUserInfoMap[userid]
|
||||
if ok {
|
||||
InviteUserInfo = append(InviteUserInfo, inviteUserInfoMap[userid])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return InviteUserInfo, nil
|
||||
}
|
||||
|
||||
func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUserID string) (showinfo *schema.QuestionInfo, err error) {
|
||||
dbinfo, has, err := qs.questionRepo.GetQuestion(ctx, questionID)
|
||||
if err != nil {
|
||||
|
@ -180,9 +214,7 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUser
|
|||
operation.Level = schema.OperationLevelInfo
|
||||
showinfo.Operation = operation
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,14 +463,16 @@ func (qs *QuestionCommon) RemoveQuestion(ctx context.Context, req *schema.Remove
|
|||
return err
|
||||
}
|
||||
|
||||
// user add question count
|
||||
err = qs.userCommon.UpdateQuestionCount(ctx, questionInfo.UserID, -1)
|
||||
userQuestionCount, err := qs.GetUserQuestionCount(ctx, questionInfo.UserID)
|
||||
if err != nil {
|
||||
log.Error("user UpdateQuestionCount error", err.Error())
|
||||
log.Error("user GetUserQuestionCount error", err.Error())
|
||||
} else {
|
||||
err = qs.userCommon.UpdateQuestionCount(ctx, questionInfo.UserID, userQuestionCount)
|
||||
if err != nil {
|
||||
log.Error("user IncreaseQuestionCount error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// todo rank remove
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -289,9 +289,14 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
|
|||
}
|
||||
|
||||
// user add question count
|
||||
err = qs.userCommon.UpdateQuestionCount(ctx, question.UserID, 1)
|
||||
userQuestionCount, err := qs.questioncommon.GetUserQuestionCount(ctx, question.UserID)
|
||||
if err != nil {
|
||||
log.Error("user IncreaseQuestionCount error", err.Error())
|
||||
log.Error("user GetUserQuestionCount error", err.Error())
|
||||
} else {
|
||||
err = qs.userCommon.UpdateQuestionCount(ctx, question.UserID, userQuestionCount)
|
||||
if err != nil {
|
||||
log.Error("user IncreaseQuestionCount error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
|
@ -327,8 +332,24 @@ func (qs *QuestionService) OperationQuestion(ctx context.Context, req *schema.Op
|
|||
switch req.Operation {
|
||||
case schema.QuestionOperationHide:
|
||||
questionInfo.Show = entity.QuestionHide
|
||||
err = qs.tagCommon.HideTagRelListByObjectID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = qs.tagCommon.RefreshTagCountByQuestionID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case schema.QuestionOperationShow:
|
||||
questionInfo.Show = entity.QuestionShow
|
||||
err = qs.tagCommon.ShowTagRelListByObjectID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = qs.tagCommon.RefreshTagCountByQuestionID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case schema.QuestionOperationPin:
|
||||
questionInfo.Pin = entity.QuestionPin
|
||||
case schema.QuestionOperationUnPin:
|
||||
|
@ -404,10 +425,33 @@ func (qs *QuestionService) RemoveQuestion(ctx context.Context, req *schema.Remov
|
|||
return err
|
||||
}
|
||||
|
||||
// user add question count
|
||||
err = qs.userCommon.UpdateQuestionCount(ctx, questionInfo.UserID, -1)
|
||||
userQuestionCount, err := qs.questioncommon.GetUserQuestionCount(ctx, questionInfo.UserID)
|
||||
if err != nil {
|
||||
log.Error("user IncreaseQuestionCount error", err.Error())
|
||||
log.Error("user GetUserQuestionCount error", err.Error())
|
||||
} else {
|
||||
err = qs.userCommon.UpdateQuestionCount(ctx, questionInfo.UserID, userQuestionCount)
|
||||
if err != nil {
|
||||
log.Error("user IncreaseQuestionCount error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
//tag count
|
||||
tagIDs := make([]string, 0)
|
||||
Tags, tagerr := qs.tagCommon.GetObjectEntityTag(ctx, req.ID)
|
||||
if tagerr != nil {
|
||||
log.Error("GetObjectEntityTag error", tagerr)
|
||||
return nil
|
||||
}
|
||||
for _, v := range Tags {
|
||||
tagIDs = append(tagIDs, v.ID)
|
||||
}
|
||||
err = qs.tagCommon.RemoveTagRelListByObjectID(ctx, req.ID)
|
||||
if err != nil {
|
||||
log.Error("RemoveTagRelListByObjectID error", err.Error())
|
||||
}
|
||||
err = qs.tagCommon.RefreshTagQuestionCount(ctx, tagIDs)
|
||||
if err != nil {
|
||||
log.Error("efreshTagQuestionCount error", err.Error())
|
||||
}
|
||||
|
||||
// #2372 In order to simplify the process and complexity, as well as to consider if it is in-house,
|
||||
|
@ -491,6 +535,54 @@ func (qs *QuestionService) UpdateQuestionCheckTags(ctx context.Context, req *sch
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (qs *QuestionService) UpdateQuestionInviteUser(ctx context.Context, req *schema.QuestionUpdateInviteUser) (err error) {
|
||||
//verify invite user
|
||||
inviteUserInfoList, err := qs.userCommon.BatchGetUserBasicInfoByUserNames(ctx, req.InviteUser)
|
||||
if err != nil {
|
||||
log.Error("BatchGetUserBasicInfoByUserNames error", err.Error())
|
||||
}
|
||||
inviteUser := make([]string, 0)
|
||||
for _, item := range req.InviteUser {
|
||||
_, ok := inviteUserInfoList[item]
|
||||
if ok {
|
||||
inviteUser = append(inviteUser, inviteUserInfoList[item].ID)
|
||||
}
|
||||
}
|
||||
inviteUserStr := ""
|
||||
inviteUserByte, err := json.Marshal(inviteUser)
|
||||
if err != nil {
|
||||
log.Error("json.Marshal error", err.Error())
|
||||
inviteUserStr = "[]"
|
||||
} else {
|
||||
inviteUserStr = string(inviteUserByte)
|
||||
}
|
||||
question := &entity.Question{}
|
||||
question.ID = uid.DeShortID(req.ID)
|
||||
question.InviteUserID = inviteUserStr
|
||||
|
||||
saveerr := qs.questionRepo.UpdateQuestion(ctx, question, []string{"invite_user_id"})
|
||||
if saveerr != nil {
|
||||
return saveerr
|
||||
}
|
||||
qs.notificationInviteUser(ctx, inviteUser, req.ID, req.UserID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qs *QuestionService) notificationInviteUser(
|
||||
ctx context.Context, invitedUserIDs []string, questionID, questionUserID string) {
|
||||
for _, userID := range invitedUserIDs {
|
||||
msg := &schema.NotificationMsg{
|
||||
ReceiverUserID: userID,
|
||||
TriggerUserID: questionUserID,
|
||||
Type: schema.NotificationTypeInbox,
|
||||
ObjectID: questionID,
|
||||
}
|
||||
msg.ObjectType = constant.QuestionObjectType
|
||||
msg.NotificationAction = constant.NotificationInvitedYouToAnswer
|
||||
notice_queue.AddNotification(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateQuestion update question
|
||||
func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.QuestionUpdate) (questionInfo any, err error) {
|
||||
var canUpdate bool
|
||||
|
@ -712,6 +804,10 @@ func (qs *QuestionService) GetQuestionAndAddPV(ctx context.Context, questionID,
|
|||
return qs.GetQuestion(ctx, questionID, loginUserID, per)
|
||||
}
|
||||
|
||||
func (qs *QuestionService) InviteUserInfo(ctx context.Context, questionID string) (inviteList []*schema.UserBasicInfo, err error) {
|
||||
return qs.questioncommon.InviteUserInfo(ctx, questionID)
|
||||
}
|
||||
|
||||
func (qs *QuestionService) ChangeTag(ctx context.Context, objectTagData *schema.TagChange) error {
|
||||
return qs.tagCommon.ObjectChangeTag(ctx, objectTagData)
|
||||
}
|
||||
|
@ -794,14 +890,18 @@ func (qs *QuestionService) PersonalAnswerPage(ctx context.Context, req *schema.P
|
|||
_, ok := questionMaps[item.QuestionID]
|
||||
if ok {
|
||||
item.QuestionInfo = questionMaps[item.QuestionID]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
info := &schema.UserAnswerInfo{}
|
||||
_ = copier.Copy(info, item)
|
||||
info.AnswerID = item.ID
|
||||
info.QuestionID = item.QuestionID
|
||||
if item.QuestionInfo.Status != entity.QuestionStatusDeleted {
|
||||
userAnswerlist = append(userAnswerlist, info)
|
||||
if item.QuestionInfo.Status == entity.QuestionStatusDeleted {
|
||||
info.QuestionInfo.Title = "Deleted question"
|
||||
|
||||
}
|
||||
userAnswerlist = append(userAnswerlist, info)
|
||||
}
|
||||
|
||||
return pager.NewPageModel(total, userAnswerlist), nil
|
||||
|
@ -835,6 +935,9 @@ func (qs *QuestionService) PersonalCollectionPage(ctx context.Context, req *sche
|
|||
questionMaps[uid.EnShortID(id)].UpdateUserInfo = nil
|
||||
questionMaps[uid.EnShortID(id)].Content = ""
|
||||
questionMaps[uid.EnShortID(id)].HTML = ""
|
||||
if questionMaps[uid.EnShortID(id)].Status == entity.QuestionStatusDeleted {
|
||||
questionMaps[uid.EnShortID(id)].Title = "Deleted question"
|
||||
}
|
||||
list = append(list, questionMaps[uid.EnShortID(id)])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,6 +269,9 @@ func (rs *RankService) GetRankPersonalWithPage(ctx context.Context, req *schema.
|
|||
commentResp.Title = objInfo.Title
|
||||
commentResp.UrlTitle = htmltext.UrlTitle(objInfo.Title)
|
||||
commentResp.Content = objInfo.Content
|
||||
if objInfo.QuestionStatus == entity.QuestionStatusDeleted {
|
||||
commentResp.Title = "Deleted question"
|
||||
}
|
||||
commentResp.QuestionID = objInfo.QuestionID
|
||||
commentResp.AnswerID = objInfo.AnswerID
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@ type TagRepo interface {
|
|||
|
||||
type TagRelRepo interface {
|
||||
AddTagRelList(ctx context.Context, tagList []*entity.TagRel) (err error)
|
||||
RemoveTagRelListByObjectID(ctx context.Context, objectID string) (err error)
|
||||
ShowTagRelListByObjectID(ctx context.Context, objectID string) (err error)
|
||||
HideTagRelListByObjectID(ctx context.Context, objectID string) (err error)
|
||||
RemoveTagRelListByIDs(ctx context.Context, ids []int64) (err error)
|
||||
EnableTagRelByIDs(ctx context.Context, ids []int64) (err error)
|
||||
GetObjectTagRelWithoutStatus(ctx context.Context, objectId, tagID string) (tagRel *entity.TagRel, exist bool, err error)
|
||||
|
@ -653,6 +656,35 @@ func (ts *TagCommonService) RefreshTagQuestionCount(ctx context.Context, tagIDs
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ts *TagCommonService) RefreshTagCountByQuestionID(ctx context.Context, questionID string) (err error) {
|
||||
tagListList, err := ts.tagRelRepo.GetObjectTagRelList(ctx, questionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tagIDs := make([]string, 0)
|
||||
for _, item := range tagListList {
|
||||
tagIDs = append(tagIDs, item.TagID)
|
||||
}
|
||||
err = ts.RefreshTagQuestionCount(ctx, tagIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveTagRelListByObjectID remove tag relation by object id
|
||||
func (ts *TagCommonService) RemoveTagRelListByObjectID(ctx context.Context, objectID string) (err error) {
|
||||
return ts.tagRelRepo.RemoveTagRelListByObjectID(ctx, objectID)
|
||||
}
|
||||
|
||||
func (ts *TagCommonService) HideTagRelListByObjectID(ctx context.Context, objectID string) (err error) {
|
||||
return ts.tagRelRepo.HideTagRelListByObjectID(ctx, objectID)
|
||||
}
|
||||
|
||||
func (ts *TagCommonService) ShowTagRelListByObjectID(ctx context.Context, objectID string) (err error) {
|
||||
return ts.tagRelRepo.ShowTagRelListByObjectID(ctx, objectID)
|
||||
}
|
||||
|
||||
// CreateOrUpdateTagRelList if tag relation is exists update status, if not create it
|
||||
func (ts *TagCommonService) CreateOrUpdateTagRelList(ctx context.Context, objectId string, tagIDs []string) (err error) {
|
||||
addTagIDMapping := make(map[string]bool)
|
||||
|
|
|
@ -20,6 +20,8 @@ type UserRepo interface {
|
|||
AddUser(ctx context.Context, user *entity.User) (err error)
|
||||
IncreaseAnswerCount(ctx context.Context, userID string, amount int) (err error)
|
||||
IncreaseQuestionCount(ctx context.Context, userID string, amount int) (err error)
|
||||
UpdateQuestionCount(ctx context.Context, userID string, count int64) (err error)
|
||||
UpdateAnswerCount(ctx context.Context, userID string, count int) (err error)
|
||||
UpdateLastLoginDate(ctx context.Context, userID string) (err error)
|
||||
UpdateEmailStatus(ctx context.Context, userID string, emailStatus int) error
|
||||
UpdateNoticeStatus(ctx context.Context, userID string, noticeStatus int) error
|
||||
|
@ -30,8 +32,10 @@ type UserRepo interface {
|
|||
GetByUserID(ctx context.Context, userID string) (userInfo *entity.User, exist bool, err error)
|
||||
BatchGetByID(ctx context.Context, ids []string) ([]*entity.User, error)
|
||||
GetByUsername(ctx context.Context, username string) (userInfo *entity.User, exist bool, err error)
|
||||
GetByUsernames(ctx context.Context, usernames []string) ([]*entity.User, error)
|
||||
GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error)
|
||||
GetUserCount(ctx context.Context) (count int64, err error)
|
||||
SearchUserListByName(ctx context.Context, name string) (userList []*entity.User, err error)
|
||||
}
|
||||
|
||||
// UserCommon user service
|
||||
|
@ -72,12 +76,25 @@ func (us *UserCommon) GetUserBasicInfoByUserName(ctx context.Context, username s
|
|||
return info, exist, nil
|
||||
}
|
||||
|
||||
func (us *UserCommon) UpdateAnswerCount(ctx context.Context, userID string, num int) error {
|
||||
return us.userRepo.IncreaseAnswerCount(ctx, userID, num)
|
||||
func (us *UserCommon) BatchGetUserBasicInfoByUserNames(ctx context.Context, usernames []string) (map[string]*schema.UserBasicInfo, error) {
|
||||
infomap := make(map[string]*schema.UserBasicInfo)
|
||||
list, err := us.userRepo.GetByUsernames(ctx, usernames)
|
||||
if err != nil {
|
||||
return infomap, err
|
||||
}
|
||||
for _, user := range list {
|
||||
info := us.FormatUserBasicInfo(ctx, user)
|
||||
infomap[user.Username] = info
|
||||
}
|
||||
return infomap, nil
|
||||
}
|
||||
|
||||
func (us *UserCommon) UpdateQuestionCount(ctx context.Context, userID string, num int) error {
|
||||
return us.userRepo.IncreaseQuestionCount(ctx, userID, num)
|
||||
func (us *UserCommon) UpdateAnswerCount(ctx context.Context, userID string, num int) error {
|
||||
return us.userRepo.UpdateAnswerCount(ctx, userID, num)
|
||||
}
|
||||
|
||||
func (us *UserCommon) UpdateQuestionCount(ctx context.Context, userID string, num int64) error {
|
||||
return us.userRepo.UpdateQuestionCount(ctx, userID, num)
|
||||
}
|
||||
|
||||
func (us *UserCommon) BatchUserBasicInfoByID(ctx context.Context, IDs []string) (map[string]*schema.UserBasicInfo, error) {
|
||||
|
|
|
@ -814,6 +814,19 @@ func (us *UserService) getUserInfoMapping(ctx context.Context, userIDs []string)
|
|||
return userInfoMapping, nil
|
||||
}
|
||||
|
||||
func (us *UserService) SearchUserListByName(ctx context.Context, name string) ([]*schema.UserBasicInfo, error) {
|
||||
userinfolist := make([]*schema.UserBasicInfo, 0)
|
||||
list, err := us.userRepo.SearchUserListByName(ctx, name)
|
||||
if err != nil {
|
||||
return userinfolist, err
|
||||
}
|
||||
for _, user := range list {
|
||||
userinfo := us.userCommonService.FormatUserBasicInfo(ctx, user)
|
||||
userinfolist = append(userinfolist, userinfo)
|
||||
}
|
||||
return userinfolist, nil
|
||||
}
|
||||
|
||||
func (us *UserService) warpStatRankingResp(
|
||||
userInfoMapping map[string]*entity.User,
|
||||
rankStat []*entity.ActivityUserRankStat,
|
||||
|
|
|
@ -195,6 +195,9 @@ func (vs *VoteService) ListUserVotes(ctx context.Context, req schema.GetVoteWith
|
|||
Content: objInfo.Content,
|
||||
VoteType: activity_type.Format(voteInfo.ActivityType),
|
||||
}
|
||||
if objInfo.QuestionStatus == entity.QuestionStatusDeleted {
|
||||
item.Title = "Deleted question"
|
||||
}
|
||||
resp = append(resp, item)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{define "header"}}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="{{.lang}}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
|
Loading…
Reference in New Issue