mirror of https://gitee.com/answerdev/answer.git
Merge branch 'dev' into feat/0.6.0/user-role
This commit is contained in:
commit
affd376f57
|
@ -27,6 +27,8 @@ stages:
|
|||
"compile the golang project":
|
||||
image: golang:1.18
|
||||
stage: compile-golang
|
||||
before_script:
|
||||
- export GOPROXY=https://goproxy.cn,direct
|
||||
script:
|
||||
- make generate
|
||||
- make build
|
||||
|
@ -36,6 +38,8 @@ stages:
|
|||
|
||||
"build docker images and push":
|
||||
stage: push
|
||||
before_script:
|
||||
- export GOPROXY=https://goproxy.cn,direct
|
||||
extends: .docker-build-push
|
||||
only:
|
||||
- test
|
||||
|
|
|
@ -31,7 +31,6 @@ import (
|
|||
"github.com/answerdev/answer/internal/repo/reason"
|
||||
"github.com/answerdev/answer/internal/repo/report"
|
||||
"github.com/answerdev/answer/internal/repo/revision"
|
||||
"github.com/answerdev/answer/internal/repo/role"
|
||||
"github.com/answerdev/answer/internal/repo/search_common"
|
||||
"github.com/answerdev/answer/internal/repo/site_info"
|
||||
"github.com/answerdev/answer/internal/repo/tag"
|
||||
|
@ -42,10 +41,12 @@ import (
|
|||
"github.com/answerdev/answer/internal/service"
|
||||
"github.com/answerdev/answer/internal/service/action"
|
||||
activity2 "github.com/answerdev/answer/internal/service/activity"
|
||||
activity_common2 "github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/answer_common"
|
||||
auth2 "github.com/answerdev/answer/internal/service/auth"
|
||||
"github.com/answerdev/answer/internal/service/collection_common"
|
||||
comment2 "github.com/answerdev/answer/internal/service/comment"
|
||||
"github.com/answerdev/answer/internal/service/comment_common"
|
||||
"github.com/answerdev/answer/internal/service/dashboard"
|
||||
export2 "github.com/answerdev/answer/internal/service/export"
|
||||
"github.com/answerdev/answer/internal/service/follow"
|
||||
|
@ -60,7 +61,6 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/report_backyard"
|
||||
"github.com/answerdev/answer/internal/service/report_handle_backyard"
|
||||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
role2 "github.com/answerdev/answer/internal/service/role"
|
||||
"github.com/answerdev/answer/internal/service/search_parser"
|
||||
"github.com/answerdev/answer/internal/service/service_config"
|
||||
"github.com/answerdev/answer/internal/service/siteinfo"
|
||||
|
@ -120,7 +120,12 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
answerRepo := answer.NewAnswerRepo(dataData, uniqueIDRepo, userRankRepo, activityRepo)
|
||||
questionRepo := question.NewQuestionRepo(dataData, uniqueIDRepo)
|
||||
tagCommonRepo := tag_common.NewTagCommonRepo(dataData, uniqueIDRepo)
|
||||
objService := object_info.NewObjService(answerRepo, questionRepo, commentCommonRepo, tagCommonRepo)
|
||||
tagRelRepo := tag.NewTagRelRepo(dataData)
|
||||
tagRepo := tag.NewTagRepo(dataData, uniqueIDRepo)
|
||||
revisionRepo := revision.NewRevisionRepo(dataData, uniqueIDRepo)
|
||||
revisionService := revision_common.NewRevisionService(revisionRepo, userRepo)
|
||||
tagCommonService := tag_common2.NewTagCommonService(tagCommonRepo, tagRelRepo, tagRepo, revisionService, siteInfoCommonService)
|
||||
objService := object_info.NewObjService(answerRepo, questionRepo, commentCommonRepo, tagCommonRepo, tagCommonService)
|
||||
voteRepo := activity_common.NewVoteRepo(dataData, activityRepo)
|
||||
commentService := comment2.NewCommentService(commentRepo, commentCommonRepo, userCommon, objService, voteRepo)
|
||||
rankService := rank2.NewRankService(userCommon, userRankRepo, objService, configRepo)
|
||||
|
@ -130,12 +135,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
reportController := controller.NewReportController(reportService, rankService)
|
||||
serviceVoteRepo := activity.NewVoteRepo(dataData, uniqueIDRepo, configRepo, activityRepo, userRankRepo, voteRepo)
|
||||
voteService := service.NewVoteService(serviceVoteRepo, uniqueIDRepo, configRepo, questionRepo, answerRepo, commentCommonRepo, objService)
|
||||
voteController := controller.NewVoteController(voteService)
|
||||
tagRepo := tag.NewTagRepo(dataData, uniqueIDRepo)
|
||||
tagRelRepo := tag.NewTagRelRepo(dataData)
|
||||
revisionRepo := revision.NewRevisionRepo(dataData, uniqueIDRepo)
|
||||
revisionService := revision_common.NewRevisionService(revisionRepo, userRepo)
|
||||
tagCommonService := tag_common2.NewTagCommonService(tagCommonRepo, tagRelRepo, revisionService, siteInfoCommonService)
|
||||
voteController := controller.NewVoteController(voteService, rankService)
|
||||
followRepo := activity_common.NewFollowRepo(dataData, uniqueIDRepo, activityRepo)
|
||||
tagService := tag2.NewTagService(tagRepo, tagCommonService, revisionService, followRepo, siteInfoCommonService)
|
||||
tagController := controller.NewTagController(tagService, tagCommonService, rankService)
|
||||
|
@ -163,19 +163,15 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
searchRepo := search_common.NewSearchRepo(dataData, uniqueIDRepo, userCommon)
|
||||
searchService := service.NewSearchService(searchParser, searchRepo)
|
||||
searchController := controller.NewSearchController(searchService)
|
||||
serviceRevisionService := service.NewRevisionService(revisionRepo, userCommon, questionCommon, answerService)
|
||||
revisionController := controller.NewRevisionController(serviceRevisionService)
|
||||
serviceRevisionService := service.NewRevisionService(revisionRepo, userCommon, questionCommon, answerService, objService, questionRepo, answerRepo, tagRepo, tagCommonService)
|
||||
revisionController := controller.NewRevisionController(serviceRevisionService, rankService)
|
||||
rankController := controller.NewRankController(rankService)
|
||||
commonRepo := common.NewCommonRepo(dataData, uniqueIDRepo)
|
||||
reportHandle := report_handle_backyard.NewReportHandle(questionCommon, commentRepo, configRepo)
|
||||
reportBackyardService := report_backyard.NewReportBackyardService(reportRepo, userCommon, commonRepo, answerRepo, questionRepo, commentCommonRepo, reportHandle, configRepo)
|
||||
controller_backyardReportController := controller_backyard.NewReportController(reportBackyardService)
|
||||
userBackyardRepo := user.NewUserBackyardRepo(dataData, authRepo)
|
||||
userRoleRelRepo := role.NewUserRoleRelRepo(dataData)
|
||||
roleRepo := role.NewRoleRepo(dataData)
|
||||
roleService := role2.NewRoleService(roleRepo)
|
||||
userRoleRelService := role2.NewUserRoleRelService(userRoleRelRepo, roleService)
|
||||
userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService)
|
||||
userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo)
|
||||
userBackyardController := controller_backyard.NewUserBackyardController(userBackyardService)
|
||||
reasonRepo := reason.NewReasonRepo(configRepo)
|
||||
reasonService := reason2.NewReasonService(reasonRepo)
|
||||
|
@ -186,12 +182,16 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
siteinfoController := controller.NewSiteinfoController(siteInfoCommonService)
|
||||
notificationRepo := notification.NewNotificationRepo(dataData)
|
||||
notificationCommon := notificationcommon.NewNotificationCommon(dataData, notificationRepo, userCommon, activityRepo, followRepo, objService)
|
||||
notificationService := notification2.NewNotificationService(dataData, notificationRepo, notificationCommon)
|
||||
notificationController := controller.NewNotificationController(notificationService)
|
||||
notificationService := notification2.NewNotificationService(dataData, notificationRepo, notificationCommon, revisionService)
|
||||
notificationController := controller.NewNotificationController(notificationService, rankService)
|
||||
dashboardController := controller.NewDashboardController(dashboardService)
|
||||
uploadController := controller.NewUploadController(uploaderService)
|
||||
roleController := controller_backyard.NewRoleController(roleService)
|
||||
answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, controller_backyardReportController, userBackyardController, reasonController, themeController, siteInfoController, siteinfoController, notificationController, dashboardController, uploadController, roleController)
|
||||
activityCommon := activity_common2.NewActivityCommon(activityRepo)
|
||||
activityActivityRepo := activity.NewActivityRepo(dataData)
|
||||
commentCommonService := comment_common.NewCommentCommonService(commentCommonRepo)
|
||||
activityService := activity2.NewActivityService(activityActivityRepo, userCommon, activityCommon, tagCommonService, objService, commentCommonService, revisionService)
|
||||
activityController := controller.NewActivityController(activityCommon, activityService)
|
||||
answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, controller_backyardReportController, userBackyardController, reasonController, themeController, siteInfoController, siteinfoController, notificationController, dashboardController, uploadController, activityController)
|
||||
swaggerRouter := router.NewSwaggerRouter(swaggerConf)
|
||||
uiRouter := router.NewUIRouter()
|
||||
authUserMiddleware := middleware.NewAuthUserMiddleware(authService)
|
||||
|
|
428
docs/docs.go
428
docs/docs.go
|
@ -1085,6 +1085,110 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/activity/timeline": {
|
||||
"get": {
|
||||
"description": "get object timeline",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Comment"
|
||||
],
|
||||
"summary": "get object timeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "object id",
|
||||
"name": "object_id",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "tag slug name",
|
||||
"name": "tag_slug_name",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"question",
|
||||
"answer",
|
||||
"tag"
|
||||
],
|
||||
"type": "string",
|
||||
"description": "object type",
|
||||
"name": "object_type",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "is show vote",
|
||||
"name": "show_vote",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/schema.GetObjectTimelineResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/activity/timeline/detail": {
|
||||
"get": {
|
||||
"description": "get object timeline detail",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Comment"
|
||||
],
|
||||
"summary": "get object timeline detail",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "revision id",
|
||||
"name": "revision_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/schema.GetObjectTimelineResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/answer": {
|
||||
"put": {
|
||||
"security": [
|
||||
|
@ -1290,12 +1394,12 @@ const docTemplate = `{
|
|||
"summary": "AnswerList",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "AnswerList",
|
||||
"description": "AnswerListReq",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.AnswerList"
|
||||
"$ref": "#/definitions/schema.AnswerListReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -3007,6 +3111,141 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/revisions/audit": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "revision audit operation:approve or reject",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Revision"
|
||||
],
|
||||
"summary": "revision audit",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "audit",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.RevisionAuditReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/revisions/edit/check": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "check can update revision",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Revision"
|
||||
],
|
||||
"summary": "check can update revision",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"default": "string",
|
||||
"description": "id",
|
||||
"name": "id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/revisions/unreviewed": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "get unreviewed revision list",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Revision"
|
||||
],
|
||||
"summary": "get unreviewed revision list",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "page id",
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/pager.PageModel"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"list": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.GetUnreviewedRevisionResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/search": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -3323,10 +3562,7 @@ const docTemplate = `{
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.GetTagSynonymsResp"
|
||||
}
|
||||
"$ref": "#/definitions/schema.GetTagSynonymsResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4548,6 +4784,67 @@ const docTemplate = `{
|
|||
"list": {}
|
||||
}
|
||||
},
|
||||
"schema.ActObjectInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"answer_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"object_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"question_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.ActObjectTimeline": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"activity_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"activity_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"cancelled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cancelled_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"object_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"object_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"revision_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.ActionRecordResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -4658,7 +4955,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.AnswerList": {
|
||||
"schema.AnswerListReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order": {
|
||||
|
@ -4954,6 +5251,20 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.GetObjectTimelineResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"object_info": {
|
||||
"$ref": "#/definitions/schema.ActObjectInfo"
|
||||
},
|
||||
"timeline": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.ActObjectTimeline"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.GetOtherUserInfoByUsernameResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -5342,21 +5653,33 @@ const docTemplate = `{
|
|||
"schema.GetTagSynonymsResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_name": {
|
||||
"description": "display name",
|
||||
"member_actions": {
|
||||
"description": "MemberActions",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.PermissionMemberAction"
|
||||
}
|
||||
},
|
||||
"synonyms": {
|
||||
"description": "synonyms",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.TagSynonym"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.GetUnreviewedRevisionResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/schema.UnreviewedRevisionInfoInfo"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"main_tag_slug_name": {
|
||||
"description": "if main tag slug name is not empty, this tag is synonymous with the main tag",
|
||||
"type": "string"
|
||||
},
|
||||
"slug_name": {
|
||||
"description": "slug name",
|
||||
"type": "string"
|
||||
},
|
||||
"tag_id": {
|
||||
"description": "tag id",
|
||||
"type": "string"
|
||||
"unreviewed_info": {
|
||||
"$ref": "#/definitions/schema.GetRevisionResp"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -5860,6 +6183,23 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.RevisionAuditReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"operation"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "object id",
|
||||
"type": "string"
|
||||
},
|
||||
"operation": {
|
||||
"description": "approve or reject",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.SearchListResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -5989,9 +6329,7 @@ const docTemplate = `{
|
|||
"type": "object",
|
||||
"required": [
|
||||
"contact_email",
|
||||
"description",
|
||||
"name",
|
||||
"short_description",
|
||||
"site_url"
|
||||
],
|
||||
"properties": {
|
||||
|
@ -6021,9 +6359,7 @@ const docTemplate = `{
|
|||
"type": "object",
|
||||
"required": [
|
||||
"contact_email",
|
||||
"description",
|
||||
"name",
|
||||
"short_description",
|
||||
"site_url"
|
||||
],
|
||||
"properties": {
|
||||
|
@ -6211,6 +6547,50 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.TagSynonym": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_name": {
|
||||
"description": "display name",
|
||||
"type": "string"
|
||||
},
|
||||
"main_tag_slug_name": {
|
||||
"description": "if main tag slug name is not empty, this tag is synonymous with the main tag",
|
||||
"type": "string"
|
||||
},
|
||||
"slug_name": {
|
||||
"description": "slug name",
|
||||
"type": "string"
|
||||
},
|
||||
"tag_id": {
|
||||
"description": "tag id",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.UnreviewedRevisionInfoInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"html": {
|
||||
"type": "string"
|
||||
},
|
||||
"object_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.TagResp"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.UpdateCommentReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
@ -1073,6 +1073,110 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/activity/timeline": {
|
||||
"get": {
|
||||
"description": "get object timeline",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Comment"
|
||||
],
|
||||
"summary": "get object timeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "object id",
|
||||
"name": "object_id",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "tag slug name",
|
||||
"name": "tag_slug_name",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"question",
|
||||
"answer",
|
||||
"tag"
|
||||
],
|
||||
"type": "string",
|
||||
"description": "object type",
|
||||
"name": "object_type",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "is show vote",
|
||||
"name": "show_vote",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/schema.GetObjectTimelineResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/activity/timeline/detail": {
|
||||
"get": {
|
||||
"description": "get object timeline detail",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Comment"
|
||||
],
|
||||
"summary": "get object timeline detail",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "revision id",
|
||||
"name": "revision_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/schema.GetObjectTimelineResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/answer": {
|
||||
"put": {
|
||||
"security": [
|
||||
|
@ -1278,12 +1382,12 @@
|
|||
"summary": "AnswerList",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "AnswerList",
|
||||
"description": "AnswerListReq",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.AnswerList"
|
||||
"$ref": "#/definitions/schema.AnswerListReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -2995,6 +3099,141 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/revisions/audit": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "revision audit operation:approve or reject",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Revision"
|
||||
],
|
||||
"summary": "revision audit",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "audit",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.RevisionAuditReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/revisions/edit/check": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "check can update revision",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Revision"
|
||||
],
|
||||
"summary": "check can update revision",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"default": "string",
|
||||
"description": "id",
|
||||
"name": "id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/revisions/unreviewed": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "get unreviewed revision list",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Revision"
|
||||
],
|
||||
"summary": "get unreviewed revision list",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "page id",
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/pager.PageModel"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"list": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.GetUnreviewedRevisionResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/search": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -3311,10 +3550,7 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.GetTagSynonymsResp"
|
||||
}
|
||||
"$ref": "#/definitions/schema.GetTagSynonymsResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4536,6 +4772,67 @@
|
|||
"list": {}
|
||||
}
|
||||
},
|
||||
"schema.ActObjectInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"answer_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"object_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"question_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.ActObjectTimeline": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"activity_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"activity_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"cancelled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cancelled_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"object_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"object_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"revision_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.ActionRecordResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -4646,7 +4943,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.AnswerList": {
|
||||
"schema.AnswerListReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order": {
|
||||
|
@ -4942,6 +5239,20 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.GetObjectTimelineResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"object_info": {
|
||||
"$ref": "#/definitions/schema.ActObjectInfo"
|
||||
},
|
||||
"timeline": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.ActObjectTimeline"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.GetOtherUserInfoByUsernameResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -5330,21 +5641,33 @@
|
|||
"schema.GetTagSynonymsResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_name": {
|
||||
"description": "display name",
|
||||
"member_actions": {
|
||||
"description": "MemberActions",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.PermissionMemberAction"
|
||||
}
|
||||
},
|
||||
"synonyms": {
|
||||
"description": "synonyms",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.TagSynonym"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.GetUnreviewedRevisionResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/schema.UnreviewedRevisionInfoInfo"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"main_tag_slug_name": {
|
||||
"description": "if main tag slug name is not empty, this tag is synonymous with the main tag",
|
||||
"type": "string"
|
||||
},
|
||||
"slug_name": {
|
||||
"description": "slug name",
|
||||
"type": "string"
|
||||
},
|
||||
"tag_id": {
|
||||
"description": "tag id",
|
||||
"type": "string"
|
||||
"unreviewed_info": {
|
||||
"$ref": "#/definitions/schema.GetRevisionResp"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -5848,6 +6171,23 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.RevisionAuditReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"operation"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "object id",
|
||||
"type": "string"
|
||||
},
|
||||
"operation": {
|
||||
"description": "approve or reject",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.SearchListResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -5977,9 +6317,7 @@
|
|||
"type": "object",
|
||||
"required": [
|
||||
"contact_email",
|
||||
"description",
|
||||
"name",
|
||||
"short_description",
|
||||
"site_url"
|
||||
],
|
||||
"properties": {
|
||||
|
@ -6009,9 +6347,7 @@
|
|||
"type": "object",
|
||||
"required": [
|
||||
"contact_email",
|
||||
"description",
|
||||
"name",
|
||||
"short_description",
|
||||
"site_url"
|
||||
],
|
||||
"properties": {
|
||||
|
@ -6199,6 +6535,50 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.TagSynonym": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_name": {
|
||||
"description": "display name",
|
||||
"type": "string"
|
||||
},
|
||||
"main_tag_slug_name": {
|
||||
"description": "if main tag slug name is not empty, this tag is synonymous with the main tag",
|
||||
"type": "string"
|
||||
},
|
||||
"slug_name": {
|
||||
"description": "slug name",
|
||||
"type": "string"
|
||||
},
|
||||
"tag_id": {
|
||||
"description": "tag id",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.UnreviewedRevisionInfoInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"html": {
|
||||
"type": "string"
|
||||
},
|
||||
"object_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/schema.TagResp"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.UpdateCommentReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
@ -89,6 +89,46 @@ definitions:
|
|||
type: integer
|
||||
list: {}
|
||||
type: object
|
||||
schema.ActObjectInfo:
|
||||
properties:
|
||||
answer_id:
|
||||
type: string
|
||||
display_name:
|
||||
type: string
|
||||
object_type:
|
||||
type: string
|
||||
question_id:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
schema.ActObjectTimeline:
|
||||
properties:
|
||||
activity_id:
|
||||
type: string
|
||||
activity_type:
|
||||
type: string
|
||||
cancelled:
|
||||
type: boolean
|
||||
cancelled_at:
|
||||
type: integer
|
||||
comment:
|
||||
type: string
|
||||
created_at:
|
||||
type: integer
|
||||
object_id:
|
||||
type: string
|
||||
object_type:
|
||||
type: string
|
||||
revision_id:
|
||||
type: string
|
||||
user_display_name:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
schema.ActionRecordResp:
|
||||
properties:
|
||||
captcha_id:
|
||||
|
@ -166,7 +206,7 @@ definitions:
|
|||
description: question_id
|
||||
type: string
|
||||
type: object
|
||||
schema.AnswerList:
|
||||
schema.AnswerListReq:
|
||||
properties:
|
||||
order:
|
||||
description: 1 Default 2 time
|
||||
|
@ -379,6 +419,15 @@ definitions:
|
|||
description: tag id
|
||||
type: string
|
||||
type: object
|
||||
schema.GetObjectTimelineResp:
|
||||
properties:
|
||||
object_info:
|
||||
$ref: '#/definitions/schema.ActObjectInfo'
|
||||
timeline:
|
||||
items:
|
||||
$ref: '#/definitions/schema.ActObjectTimeline'
|
||||
type: array
|
||||
type: object
|
||||
schema.GetOtherUserInfoByUsernameResp:
|
||||
properties:
|
||||
answer_count:
|
||||
|
@ -659,19 +708,25 @@ definitions:
|
|||
type: object
|
||||
schema.GetTagSynonymsResp:
|
||||
properties:
|
||||
display_name:
|
||||
description: display name
|
||||
type: string
|
||||
main_tag_slug_name:
|
||||
description: if main tag slug name is not empty, this tag is synonymous with
|
||||
the main tag
|
||||
type: string
|
||||
slug_name:
|
||||
description: slug name
|
||||
type: string
|
||||
tag_id:
|
||||
description: tag id
|
||||
member_actions:
|
||||
description: MemberActions
|
||||
items:
|
||||
$ref: '#/definitions/schema.PermissionMemberAction'
|
||||
type: array
|
||||
synonyms:
|
||||
description: synonyms
|
||||
items:
|
||||
$ref: '#/definitions/schema.TagSynonym'
|
||||
type: array
|
||||
type: object
|
||||
schema.GetUnreviewedRevisionResp:
|
||||
properties:
|
||||
info:
|
||||
$ref: '#/definitions/schema.UnreviewedRevisionInfoInfo'
|
||||
type:
|
||||
type: string
|
||||
unreviewed_info:
|
||||
$ref: '#/definitions/schema.GetRevisionResp'
|
||||
type: object
|
||||
schema.GetUserPageResp:
|
||||
properties:
|
||||
|
@ -1039,6 +1094,18 @@ definitions:
|
|||
- flagged_type
|
||||
- id
|
||||
type: object
|
||||
schema.RevisionAuditReq:
|
||||
properties:
|
||||
id:
|
||||
description: object id
|
||||
type: string
|
||||
operation:
|
||||
description: approve or reject
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- operation
|
||||
type: object
|
||||
schema.SearchListResp:
|
||||
properties:
|
||||
count:
|
||||
|
@ -1145,9 +1212,7 @@ definitions:
|
|||
type: string
|
||||
required:
|
||||
- contact_email
|
||||
- description
|
||||
- name
|
||||
- short_description
|
||||
- site_url
|
||||
type: object
|
||||
schema.SiteGeneralResp:
|
||||
|
@ -1169,9 +1234,7 @@ definitions:
|
|||
type: string
|
||||
required:
|
||||
- contact_email
|
||||
- description
|
||||
- name
|
||||
- short_description
|
||||
- site_url
|
||||
type: object
|
||||
schema.SiteInterfaceReq:
|
||||
|
@ -1286,6 +1349,37 @@ definitions:
|
|||
slug_name:
|
||||
type: string
|
||||
type: object
|
||||
schema.TagSynonym:
|
||||
properties:
|
||||
display_name:
|
||||
description: display name
|
||||
type: string
|
||||
main_tag_slug_name:
|
||||
description: if main tag slug name is not empty, this tag is synonymous with
|
||||
the main tag
|
||||
type: string
|
||||
slug_name:
|
||||
description: slug name
|
||||
type: string
|
||||
tag_id:
|
||||
description: tag id
|
||||
type: string
|
||||
type: object
|
||||
schema.UnreviewedRevisionInfoInfo:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
html:
|
||||
type: string
|
||||
object_id:
|
||||
type: string
|
||||
tags:
|
||||
items:
|
||||
$ref: '#/definitions/schema.TagResp'
|
||||
type: array
|
||||
title:
|
||||
type: string
|
||||
type: object
|
||||
schema.UpdateCommentReq:
|
||||
properties:
|
||||
comment_id:
|
||||
|
@ -2264,6 +2358,69 @@ paths:
|
|||
summary: get user page
|
||||
tags:
|
||||
- admin
|
||||
/answer/api/v1/activity/timeline:
|
||||
get:
|
||||
description: get object timeline
|
||||
parameters:
|
||||
- description: object id
|
||||
in: query
|
||||
name: object_id
|
||||
type: string
|
||||
- description: tag slug name
|
||||
in: query
|
||||
name: tag_slug_name
|
||||
type: string
|
||||
- description: object type
|
||||
enum:
|
||||
- question
|
||||
- answer
|
||||
- tag
|
||||
in: query
|
||||
name: object_type
|
||||
type: string
|
||||
- description: is show vote
|
||||
in: query
|
||||
name: show_vote
|
||||
type: boolean
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/handler.RespBody'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/schema.GetObjectTimelineResp'
|
||||
type: object
|
||||
summary: get object timeline
|
||||
tags:
|
||||
- Comment
|
||||
/answer/api/v1/activity/timeline/detail:
|
||||
get:
|
||||
description: get object timeline detail
|
||||
parameters:
|
||||
- description: revision id
|
||||
in: query
|
||||
name: revision_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/handler.RespBody'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/schema.GetObjectTimelineResp'
|
||||
type: object
|
||||
summary: get object timeline detail
|
||||
tags:
|
||||
- Comment
|
||||
/answer/api/v1/answer:
|
||||
delete:
|
||||
consumes:
|
||||
|
@ -2386,12 +2543,12 @@ paths:
|
|||
- application/json
|
||||
description: AnswerList <br> <b>order</b> (default or updated)
|
||||
parameters:
|
||||
- description: AnswerList
|
||||
- description: AnswerListReq
|
||||
in: body
|
||||
name: data
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/schema.AnswerList'
|
||||
$ref: '#/definitions/schema.AnswerListReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
@ -3437,6 +3594,85 @@ paths:
|
|||
summary: get revision list
|
||||
tags:
|
||||
- Revision
|
||||
/answer/api/v1/revisions/audit:
|
||||
put:
|
||||
description: revision audit operation:approve or reject
|
||||
parameters:
|
||||
- description: audit
|
||||
in: body
|
||||
name: data
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/schema.RevisionAuditReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.RespBody'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: revision audit
|
||||
tags:
|
||||
- Revision
|
||||
/answer/api/v1/revisions/edit/check:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: check can update revision
|
||||
parameters:
|
||||
- default: string
|
||||
description: id
|
||||
in: query
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.RespBody'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: check can update revision
|
||||
tags:
|
||||
- Revision
|
||||
/answer/api/v1/revisions/unreviewed:
|
||||
get:
|
||||
description: get unreviewed revision list
|
||||
parameters:
|
||||
- description: page id
|
||||
in: query
|
||||
name: page
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/handler.RespBody'
|
||||
- properties:
|
||||
data:
|
||||
allOf:
|
||||
- $ref: '#/definitions/pager.PageModel'
|
||||
- properties:
|
||||
list:
|
||||
items:
|
||||
$ref: '#/definitions/schema.GetUnreviewedRevisionResp'
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: get unreviewed revision list
|
||||
tags:
|
||||
- Revision
|
||||
/answer/api/v1/search:
|
||||
get:
|
||||
description: search object
|
||||
|
@ -3632,9 +3868,7 @@ paths:
|
|||
- $ref: '#/definitions/handler.RespBody'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/schema.GetTagSynonymsResp'
|
||||
type: array
|
||||
$ref: '#/definitions/schema.GetTagSynonymsResp'
|
||||
type: object
|
||||
summary: get tag synonyms
|
||||
tags:
|
||||
|
|
2
go.mod
2
go.mod
|
@ -65,6 +65,7 @@ require (
|
|||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/subcommands v1.0.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
|
@ -105,6 +106,7 @@ require (
|
|||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.23.0 // indirect
|
||||
golang.org/x/image v0.1.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -284,6 +284,7 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe
|
|||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
@ -779,6 +780,7 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# The following fields are used for back-end
|
||||
|
||||
backend:
|
||||
base:
|
||||
success:
|
||||
|
@ -42,6 +43,10 @@ backend:
|
|||
answer:
|
||||
not_found:
|
||||
other: "Answer do not found."
|
||||
cannot_deleted:
|
||||
other: "No permission to delete."
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
comment:
|
||||
edit_without_permission:
|
||||
other: "Comment are not allowed to edit."
|
||||
|
@ -79,6 +84,12 @@ backend:
|
|||
question:
|
||||
not_found:
|
||||
other: "Question not found."
|
||||
cannot_deleted:
|
||||
other: "No permission to delete."
|
||||
cannot_close:
|
||||
other: "No permission to close."
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
rank:
|
||||
fail_to_meet_the_condition:
|
||||
other: "Rank fail to meet the condition."
|
||||
|
@ -92,11 +103,20 @@ backend:
|
|||
other: "Tag not found."
|
||||
recommend_tag_not_found:
|
||||
other: "Recommend Tag is not exist."
|
||||
recommend_tag_enter:
|
||||
other: "Please enter at least one required tag."
|
||||
not_contain_synonym_tags:
|
||||
other: "Should not contain synonym tags."
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
theme:
|
||||
not_found:
|
||||
other: "Theme not found."
|
||||
revision:
|
||||
review_underway:
|
||||
other: "Can't edit currently, there is a version in the review queue."
|
||||
no_permission:
|
||||
other: "No permission to Revision."
|
||||
user:
|
||||
email_or_password_wrong:
|
||||
other: *email_or_password_wrong
|
||||
|
@ -110,18 +130,17 @@ backend:
|
|||
other: "Username is already in use."
|
||||
set_avatar:
|
||||
other: "Avatar set failed."
|
||||
|
||||
config:
|
||||
read_config_failed:
|
||||
other: "Read config failed"
|
||||
database:
|
||||
connection_failed:
|
||||
other: "Database connection failed"
|
||||
create_table_failed:
|
||||
other: "Create table failed"
|
||||
install:
|
||||
create_config_failed:
|
||||
other: "Can’t create the config.yaml file."
|
||||
config:
|
||||
read_config_failed:
|
||||
other: "Read config failed"
|
||||
database:
|
||||
connection_failed:
|
||||
other: "Database connection failed"
|
||||
create_table_failed:
|
||||
other: "Create table failed"
|
||||
install:
|
||||
create_config_failed:
|
||||
other: "Can’t create the config.yaml file."
|
||||
report:
|
||||
spam:
|
||||
name:
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package constant
|
||||
|
||||
// question activity
|
||||
|
||||
type ActivityTypeKey string
|
||||
|
||||
const (
|
||||
ActQuestionAsked ActivityTypeKey = "question.asked"
|
||||
ActQuestionClosed ActivityTypeKey = "question.closed"
|
||||
ActQuestionReopened ActivityTypeKey = "question.reopened"
|
||||
ActQuestionAnswered ActivityTypeKey = "question.answered"
|
||||
ActQuestionCommented ActivityTypeKey = "question.commented"
|
||||
ActQuestionAccept ActivityTypeKey = "question.accept"
|
||||
ActQuestionUpvote ActivityTypeKey = "question.upvote"
|
||||
ActQuestionDownvote ActivityTypeKey = "question.downvote"
|
||||
ActQuestionEdited ActivityTypeKey = "question.edited"
|
||||
ActQuestionRollback ActivityTypeKey = "question.rollback"
|
||||
ActQuestionDeleted ActivityTypeKey = "question.deleted"
|
||||
ActQuestionUndeleted ActivityTypeKey = "question.undeleted"
|
||||
)
|
||||
|
||||
// answer activity
|
||||
|
||||
const (
|
||||
ActAnswerAnswered ActivityTypeKey = "answer.answered"
|
||||
ActAnswerCommented ActivityTypeKey = "answer.commented"
|
||||
ActAnswerAccept ActivityTypeKey = "answer.accept"
|
||||
ActAnswerUpvote ActivityTypeKey = "answer.upvote"
|
||||
ActAnswerDownvote ActivityTypeKey = "answer.downvote"
|
||||
ActAnswerEdited ActivityTypeKey = "answer.edited"
|
||||
ActAnswerRollback ActivityTypeKey = "answer.rollback"
|
||||
ActAnswerDeleted ActivityTypeKey = "answer.deleted"
|
||||
ActAnswerUndeleted ActivityTypeKey = "answer.undeleted"
|
||||
)
|
||||
|
||||
// tag activity
|
||||
|
||||
const (
|
||||
ActTagCreated ActivityTypeKey = "tag.created"
|
||||
ActTagEdited ActivityTypeKey = "tag.edited"
|
||||
ActTagRollback ActivityTypeKey = "tag.rollback"
|
||||
ActTagDeleted ActivityTypeKey = "tag.deleted"
|
||||
ActTagUndeleted ActivityTypeKey = "tag.undeleted"
|
||||
)
|
|
@ -113,15 +113,20 @@ func (am *AuthUserMiddleware) CmsAuth() gin.HandlerFunc {
|
|||
|
||||
// GetLoginUserIDFromContext get user id from context
|
||||
func GetLoginUserIDFromContext(ctx *gin.Context) (userID string) {
|
||||
userInfo, exist := ctx.Get(ctxUUIDKey)
|
||||
if !exist {
|
||||
userInfo := GetUserInfoFromContext(ctx)
|
||||
if userInfo == nil {
|
||||
return ""
|
||||
}
|
||||
u, ok := userInfo.(*entity.UserCacheInfo)
|
||||
if !ok {
|
||||
return ""
|
||||
return userInfo.UserID
|
||||
}
|
||||
|
||||
// GetIsAdminFromContext get user is admin from context
|
||||
func GetIsAdminFromContext(ctx *gin.Context) (isAdmin bool) {
|
||||
userInfo := GetUserInfoFromContext(ctx)
|
||||
if userInfo == nil {
|
||||
return false
|
||||
}
|
||||
return u.UserID
|
||||
return userInfo.IsAdmin
|
||||
}
|
||||
|
||||
// GetUserInfoFromContext get user info from context
|
||||
|
|
|
@ -14,38 +14,47 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
EmailOrPasswordWrong = "error.object.email_or_password_incorrect"
|
||||
CommentNotFound = "error.comment.not_found"
|
||||
QuestionNotFound = "error.question.not_found"
|
||||
AnswerNotFound = "error.answer.not_found"
|
||||
CommentEditWithoutPermission = "error.comment.edit_without_permission"
|
||||
DisallowVote = "error.object.disallow_vote"
|
||||
DisallowFollow = "error.object.disallow_follow"
|
||||
DisallowVoteYourSelf = "error.object.disallow_vote_your_self"
|
||||
CaptchaVerificationFailed = "error.object.captcha_verification_failed"
|
||||
EmailOrPasswordWrong = "error.object.email_or_password_incorrect"
|
||||
CommentNotFound = "error.comment.not_found"
|
||||
QuestionNotFound = "error.question.not_found"
|
||||
QuestionCannotDeleted = "error.question.cannot_deleted"
|
||||
QuestionCannotClose = "error.question.cannot_close"
|
||||
QuestionCannotUpdate = "error.question.cannot_update"
|
||||
AnswerNotFound = "error.answer.not_found"
|
||||
AnswerCannotDeleted = "error.answer.cannot_deleted"
|
||||
AnswerCannotUpdate = "error.answer.cannot_update"
|
||||
CommentEditWithoutPermission = "error.comment.edit_without_permission"
|
||||
DisallowVote = "error.object.disallow_vote"
|
||||
DisallowFollow = "error.object.disallow_follow"
|
||||
DisallowVoteYourSelf = "error.object.disallow_vote_your_self"
|
||||
CaptchaVerificationFailed = "error.object.captcha_verification_failed"
|
||||
OldPasswordVerificationFailed = "error.object.old_password_verification_failed"
|
||||
NewPasswordSameAsPreviousSetting = "error.object.new_password_same_as_previous_setting"
|
||||
UserNotFound = "error.user.not_found"
|
||||
UsernameInvalid = "error.user.username_invalid"
|
||||
UsernameDuplicate = "error.user.username_duplicate"
|
||||
UserSetAvatar = "error.user.set_avatar"
|
||||
EmailDuplicate = "error.email.duplicate"
|
||||
EmailVerifyURLExpired = "error.email.verify_url_expired"
|
||||
EmailNeedToBeVerified = "error.email.need_to_be_verified"
|
||||
UserSuspended = "error.user.suspended"
|
||||
ObjectNotFound = "error.object.not_found"
|
||||
TagNotFound = "error.tag.not_found"
|
||||
TagNotContainSynonym = "error.tag.not_contain_synonym_tags"
|
||||
RankFailToMeetTheCondition = "error.rank.fail_to_meet_the_condition"
|
||||
ThemeNotFound = "error.theme.not_found"
|
||||
LangNotFound = "error.lang.not_found"
|
||||
ReportHandleFailed = "error.report.handle_failed"
|
||||
ReportNotFound = "error.report.not_found"
|
||||
ReadConfigFailed = "error.config.read_config_failed"
|
||||
DatabaseConnectionFailed = "error.database.connection_failed"
|
||||
InstallCreateTableFailed = "error.database.create_table_failed"
|
||||
InstallConfigFailed = "error.install.create_config_failed"
|
||||
SiteInfoNotFound = "error.site_info.not_found"
|
||||
UploadFileSourceUnsupported = "error.upload.source_unsupported"
|
||||
RecommendTagNotExist = "error.tag.recommend_tag_not_found"
|
||||
UserNotFound = "error.user.not_found"
|
||||
UsernameInvalid = "error.user.username_invalid"
|
||||
UsernameDuplicate = "error.user.username_duplicate"
|
||||
UserSetAvatar = "error.user.set_avatar"
|
||||
EmailDuplicate = "error.email.duplicate"
|
||||
EmailVerifyURLExpired = "error.email.verify_url_expired"
|
||||
EmailNeedToBeVerified = "error.email.need_to_be_verified"
|
||||
UserSuspended = "error.user.suspended"
|
||||
ObjectNotFound = "error.object.not_found"
|
||||
TagNotFound = "error.tag.not_found"
|
||||
TagNotContainSynonym = "error.tag.not_contain_synonym_tags"
|
||||
TagCannotUpdate = "error.tag.cannot_update"
|
||||
RankFailToMeetTheCondition = "error.rank.fail_to_meet_the_condition"
|
||||
ThemeNotFound = "error.theme.not_found"
|
||||
LangNotFound = "error.lang.not_found"
|
||||
ReportHandleFailed = "error.report.handle_failed"
|
||||
ReportNotFound = "error.report.not_found"
|
||||
ReadConfigFailed = "error.config.read_config_failed"
|
||||
DatabaseConnectionFailed = "error.database.connection_failed"
|
||||
InstallCreateTableFailed = "error.database.create_table_failed"
|
||||
InstallConfigFailed = "error.install.create_config_failed"
|
||||
SiteInfoNotFound = "error.site_info.not_found"
|
||||
UploadFileSourceUnsupported = "error.upload.source_unsupported"
|
||||
RecommendTagNotExist = "error.tag.recommend_tag_not_found"
|
||||
RecommendTagEnter = "error.tag.recommend_tag_enter"
|
||||
RevisionReviewUnderway = "error.revision.review_underway"
|
||||
RevisionNoPermission = "error.revision.no_permission"
|
||||
)
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
"github.com/answerdev/answer/internal/base/middleware"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/activity"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ActivityController struct {
|
||||
activityCommonService *activity_common.ActivityCommon
|
||||
activityService *activity.ActivityService
|
||||
}
|
||||
|
||||
// NewActivityController new activity controller.
|
||||
func NewActivityController(
|
||||
activityCommonService *activity_common.ActivityCommon,
|
||||
activityService *activity.ActivityService) *ActivityController {
|
||||
return &ActivityController{activityCommonService: activityCommonService, activityService: activityService}
|
||||
}
|
||||
|
||||
// GetObjectTimeline get object timeline
|
||||
// @Summary get object timeline
|
||||
// @Description get object timeline
|
||||
// @Tags Comment
|
||||
// @Produce json
|
||||
// @Param object_id query string false "object id"
|
||||
// @Param tag_slug_name query string false "tag slug name"
|
||||
// @Param object_type query string false "object type" Enums(question, answer, tag)
|
||||
// @Param show_vote query boolean false "is show vote"
|
||||
// @Success 200 {object} handler.RespBody{data=schema.GetObjectTimelineResp}
|
||||
// @Router /answer/api/v1/activity/timeline [get]
|
||||
func (ac *ActivityController) GetObjectTimeline(ctx *gin.Context) {
|
||||
req := &schema.GetObjectTimelineReq{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
resp, err := ac.activityService.GetObjectTimeline(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
||||
// GetObjectTimelineDetail get object timeline detail
|
||||
// @Summary get object timeline detail
|
||||
// @Description get object timeline detail
|
||||
// @Tags Comment
|
||||
// @Produce json
|
||||
// @Param revision_id query string true "revision id"
|
||||
// @Success 200 {object} handler.RespBody{data=schema.GetObjectTimelineResp}
|
||||
// @Router /answer/api/v1/activity/timeline/detail [get]
|
||||
func (ac *ActivityController) GetObjectTimelineDetail(ctx *gin.Context) {
|
||||
req := &schema.GetObjectTimelineDetailReq{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
resp, err := ac.activityService.GetObjectTimelineDetail(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
|
@ -51,12 +51,18 @@ func (ac *AnswerController) RemoveAnswer(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := ac.rankService.CheckRankPermission(ctx, req.UserID, rank.AnswerDeleteRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
|
||||
can, err := ac.rankService.CheckOperationPermission(ctx, req.UserID, rank.AnswerDeleteRank, req.ID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := ac.answerService.RemoveAnswer(ctx, req)
|
||||
err = ac.answerService.RemoveAnswer(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
@ -105,8 +111,13 @@ func (ac *AnswerController) Add(ctx *gin.Context) {
|
|||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
if can, err := ac.rankService.CheckRankPermission(ctx, req.UserID, rank.AnswerAddRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
can, err := ac.rankService.CheckOperationPermission(ctx, req.UserID, rank.AnswerAddRank, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -148,30 +159,32 @@ func (ac *AnswerController) Update(ctx *gin.Context) {
|
|||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
if can, err := ac.rankService.CheckRankPermission(ctx, req.UserID, rank.AnswerEditRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
canList, err := ac.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.AnswerEditRank,
|
||||
rank.AnswerEditWithoutReviewRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanEdit = canList[0]
|
||||
req.NoNeedReview = canList[1]
|
||||
if !req.CanEdit {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := ac.answerService.Update(ctx, req)
|
||||
_, err = ac.answerService.Update(ctx, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
info, questionInfo, has, err := ac.answerService.Get(ctx, req.ID, req.UserID)
|
||||
_, _, _, err = ac.answerService.Get(ctx, req.ID, req.UserID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !has {
|
||||
// todo !has
|
||||
handler.HandleResponse(ctx, nil, nil)
|
||||
return
|
||||
}
|
||||
handler.HandleResponse(ctx, nil, gin.H{
|
||||
"info": info,
|
||||
"question": questionInfo,
|
||||
})
|
||||
handler.HandleResponse(ctx, nil, &schema.AnswerUpdateResp{WaitForReview: !req.NoNeedReview})
|
||||
}
|
||||
|
||||
// AnswerList godoc
|
||||
|
@ -181,15 +194,27 @@ func (ac *AnswerController) Update(ctx *gin.Context) {
|
|||
// @Security ApiKeyAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body schema.AnswerList true "AnswerList"
|
||||
// @Param data body schema.AnswerListReq true "AnswerListReq"
|
||||
// @Success 200 {string} string ""
|
||||
// @Router /answer/api/v1/answer/list [get]
|
||||
func (ac *AnswerController) AnswerList(ctx *gin.Context) {
|
||||
req := &schema.AnswerList{}
|
||||
req := &schema.AnswerListReq{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
canList, err := ac.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.AnswerEditRank,
|
||||
rank.AnswerDeleteRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanEdit = canList[0]
|
||||
req.CanDelete = canList[1]
|
||||
|
||||
list, count, err := ac.answerService.SearchList(ctx, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
|
@ -218,12 +243,17 @@ func (ac *AnswerController) Adopted(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := ac.rankService.CheckRankPermission(ctx, req.UserID, rank.AnswerAcceptRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
can, err := ac.rankService.CheckOperationPermission(ctx, req.UserID, rank.AnswerAcceptRank, req.QuestionID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := ac.answerService.UpdateAdopted(ctx, req)
|
||||
err = ac.answerService.UpdateAdopted(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -41,8 +41,20 @@ func (cc *CommentController) AddComment(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := cc.rankService.CheckRankPermission(ctx, req.UserID, rank.CommentAddRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.CommentAddRank,
|
||||
rank.CommentEditRank,
|
||||
rank.CommentDeleteRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanAdd = canList[0]
|
||||
req.CanEdit = canList[1]
|
||||
req.CanDelete = canList[2]
|
||||
if !req.CanAdd {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -67,12 +79,17 @@ func (cc *CommentController) RemoveComment(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := cc.rankService.CheckRankPermission(ctx, req.UserID, rank.CommentDeleteRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
can, err := cc.rankService.CheckOperationPermission(ctx, req.UserID, rank.CommentDeleteRank, req.CommentID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := cc.commentService.RemoveComment(ctx, req)
|
||||
err = cc.commentService.RemoveComment(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
@ -93,12 +110,17 @@ func (cc *CommentController) UpdateComment(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := cc.rankService.CheckRankPermission(ctx, req.UserID, rank.CommentEditRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
can, err := cc.rankService.CheckOperationPermission(ctx, req.UserID, rank.CommentEditRank, req.CommentID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := cc.commentService.UpdateComment(ctx, req)
|
||||
err = cc.commentService.UpdateComment(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
@ -120,6 +142,16 @@ func (cc *CommentController) GetCommentWithPage(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.CommentEditRank,
|
||||
rank.CommentDeleteRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanEdit = canList[0]
|
||||
req.CanDelete = canList[1]
|
||||
|
||||
resp, err := cc.commentService.GetCommentWithPage(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
|
@ -162,6 +194,16 @@ func (cc *CommentController) GetComment(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.CommentEditRank,
|
||||
rank.CommentDeleteRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanEdit = canList[0]
|
||||
req.CanDelete = canList[1]
|
||||
|
||||
resp, err := cc.commentService.GetComment(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
|
|
|
@ -22,4 +22,5 @@ var ProviderSetController = wire.NewSet(
|
|||
NewSiteinfoController,
|
||||
NewDashboardController,
|
||||
NewUploadController,
|
||||
NewActivityController,
|
||||
)
|
||||
|
|
|
@ -5,17 +5,25 @@ import (
|
|||
"github.com/answerdev/answer/internal/base/middleware"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/notification"
|
||||
"github.com/answerdev/answer/internal/service/rank"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// NotificationController notification controller
|
||||
type NotificationController struct {
|
||||
notificationService *notification.NotificationService
|
||||
rankService *rank.RankService
|
||||
}
|
||||
|
||||
// NewNotificationController new controller
|
||||
func NewNotificationController(notificationService *notification.NotificationService) *NotificationController {
|
||||
return &NotificationController{notificationService: notificationService}
|
||||
func NewNotificationController(
|
||||
notificationService *notification.NotificationService,
|
||||
rankService *rank.RankService,
|
||||
) *NotificationController {
|
||||
return &NotificationController{
|
||||
notificationService: notificationService,
|
||||
rankService: rankService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetRedDot
|
||||
|
@ -28,8 +36,26 @@ func NewNotificationController(notificationService *notification.NotificationSer
|
|||
// @Success 200 {object} handler.RespBody
|
||||
// @Router /answer/api/v1/notification/status [get]
|
||||
func (nc *NotificationController) GetRedDot(ctx *gin.Context) {
|
||||
|
||||
req := &schema.GetRedDot{}
|
||||
|
||||
userID := middleware.GetLoginUserIDFromContext(ctx)
|
||||
RedDot, err := nc.notificationService.GetRedDot(ctx, userID)
|
||||
req.UserID = userID
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
canList, err := nc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.QuestionAuditRank,
|
||||
rank.AnswerAuditRank,
|
||||
rank.TagAuditRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanReviewQuestion = canList[0]
|
||||
req.CanReviewAnswer = canList[1]
|
||||
req.CanReviewTag = canList[2]
|
||||
|
||||
RedDot, err := nc.notificationService.GetRedDot(ctx, req)
|
||||
handler.HandleResponse(ctx, err, RedDot)
|
||||
}
|
||||
|
||||
|
@ -48,8 +74,21 @@ func (nc *NotificationController) ClearRedDot(ctx *gin.Context) {
|
|||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
userID := middleware.GetLoginUserIDFromContext(ctx)
|
||||
RedDot, err := nc.notificationService.ClearRedDot(ctx, userID, req.TypeStr)
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
canList, err := nc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.QuestionAuditRank,
|
||||
rank.AnswerAuditRank,
|
||||
rank.TagAuditRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanReviewQuestion = canList[0]
|
||||
req.CanReviewAnswer = canList[1]
|
||||
req.CanReviewTag = canList[2]
|
||||
|
||||
RedDot, err := nc.notificationService.ClearRedDot(ctx, req)
|
||||
handler.HandleResponse(ctx, err, RedDot)
|
||||
}
|
||||
|
||||
|
|
|
@ -42,12 +42,18 @@ func (qc *QuestionController) RemoveQuestion(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := qc.rankService.CheckRankPermission(ctx, req.UserID, rank.QuestionDeleteRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
|
||||
can, err := qc.rankService.CheckOperationPermission(ctx, req.UserID, rank.QuestionDeleteRank, req.ID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := qc.questionService.RemoveQuestion(ctx, req)
|
||||
err = qc.questionService.RemoveQuestion(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
@ -67,6 +73,7 @@ func (qc *QuestionController) CloseQuestion(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
|
||||
err := qc.questionService.CloseQuestion(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
@ -81,16 +88,28 @@ func (qc *QuestionController) CloseQuestion(ctx *gin.Context) {
|
|||
// @Param id query string true "Question TagID" default(1)
|
||||
// @Success 200 {string} string ""
|
||||
// @Router /answer/api/v1/question/info [get]
|
||||
func (qc *QuestionController) GetQuestion(c *gin.Context) {
|
||||
id := c.Query("id")
|
||||
ctx := context.Background()
|
||||
userID := middleware.GetLoginUserIDFromContext(c)
|
||||
info, err := qc.questionService.GetQuestion(ctx, id, userID, true)
|
||||
func (qc *QuestionController) GetQuestion(ctx *gin.Context) {
|
||||
id := ctx.Query("id")
|
||||
userID := middleware.GetLoginUserIDFromContext(ctx)
|
||||
req := schema.QuestionPermission{}
|
||||
canList, err := qc.rankService.CheckOperationPermissions(ctx, userID, []string{
|
||||
rank.QuestionEditRank,
|
||||
rank.QuestionDeleteRank,
|
||||
}, id)
|
||||
if err != nil {
|
||||
handler.HandleResponse(c, err, nil)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
handler.HandleResponse(c, nil, info)
|
||||
req.CanEdit = canList[0]
|
||||
req.CanDelete = canList[1]
|
||||
req.CanClose = middleware.GetIsAdminFromContext(ctx)
|
||||
|
||||
info, err := qc.questionService.GetQuestionAndAddPV(ctx, id, userID, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
handler.HandleResponse(ctx, nil, info)
|
||||
}
|
||||
|
||||
// SimilarQuestion godoc
|
||||
|
@ -188,8 +207,21 @@ func (qc *QuestionController) AddQuestion(ctx *gin.Context) {
|
|||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
if can, err := qc.rankService.CheckRankPermission(ctx, req.UserID, rank.QuestionAddRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.QuestionAddRank,
|
||||
rank.QuestionEditRank,
|
||||
rank.QuestionDeleteRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanAdd = canList[0]
|
||||
req.CanEdit = canList[1]
|
||||
req.CanDelete = canList[2]
|
||||
req.CanClose = middleware.GetIsAdminFromContext(ctx)
|
||||
if !req.CanAdd {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -214,13 +246,28 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
|
|||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
if can, err := qc.rankService.CheckRankPermission(ctx, req.UserID, rank.QuestionEditRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.QuestionEditRank,
|
||||
rank.QuestionDeleteRank,
|
||||
rank.QuestionEditWithoutReviewRank,
|
||||
}, req.ID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanEdit = canList[0]
|
||||
req.CanDelete = canList[1]
|
||||
req.NoNeedReview = canList[2]
|
||||
|
||||
req.CanClose = middleware.GetIsAdminFromContext(ctx)
|
||||
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
|
||||
if !req.CanEdit {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := qc.questionService.UpdateQuestion(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
_, err = qc.questionService.UpdateQuestion(ctx, req)
|
||||
handler.HandleResponse(ctx, err, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview})
|
||||
}
|
||||
|
||||
// CloseMsgList close question msg list
|
||||
|
|
|
@ -40,12 +40,17 @@ func (rc *ReportController) AddReport(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := rc.rankService.CheckRankPermission(ctx, req.UserID, rank.ReportAddRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
can, err := rc.rankService.CheckOperationPermission(ctx, req.UserID, rank.ReportAddRank, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := rc.reportService.AddReport(ctx, req)
|
||||
err = rc.reportService.AddReport(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
"github.com/answerdev/answer/internal/base/middleware"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service"
|
||||
"github.com/answerdev/answer/internal/service/rank"
|
||||
"github.com/answerdev/answer/pkg/obj"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
@ -12,11 +16,18 @@ import (
|
|||
// RevisionController revision controller
|
||||
type RevisionController struct {
|
||||
revisionListService *service.RevisionService
|
||||
rankService *rank.RankService
|
||||
}
|
||||
|
||||
// NewRevisionController new controller
|
||||
func NewRevisionController(revisionListService *service.RevisionService) *RevisionController {
|
||||
return &RevisionController{revisionListService: revisionListService}
|
||||
func NewRevisionController(
|
||||
revisionListService *service.RevisionService,
|
||||
rankService *rank.RankService,
|
||||
) *RevisionController {
|
||||
return &RevisionController{
|
||||
revisionListService: revisionListService,
|
||||
rankService: rankService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetRevisionList godoc
|
||||
|
@ -41,3 +52,113 @@ func (rc *RevisionController) GetRevisionList(ctx *gin.Context) {
|
|||
resp, err := rc.revisionListService.GetRevisionList(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
||||
// GetUnreviewedRevisionList godoc
|
||||
// @Summary get unreviewed revision list
|
||||
// @Description get unreviewed revision list
|
||||
// @Tags Revision
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param page query string true "page id"
|
||||
// @Success 200 {object} handler.RespBody{data=pager.PageModel{list=[]schema.GetUnreviewedRevisionResp}}
|
||||
// @Router /answer/api/v1/revisions/unreviewed [get]
|
||||
func (rc *RevisionController) GetUnreviewedRevisionList(ctx *gin.Context) {
|
||||
req := &schema.RevisionSearch{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
canList, err := rc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.QuestionAuditRank,
|
||||
rank.AnswerAuditRank,
|
||||
rank.TagAuditRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanReviewQuestion = canList[0]
|
||||
req.CanReviewAnswer = canList[1]
|
||||
req.CanReviewTag = canList[2]
|
||||
|
||||
resp, err := rc.revisionListService.GetUnreviewedRevisionPage(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
||||
// RevisionAudit godoc
|
||||
// @Summary revision audit
|
||||
// @Description revision audit operation:approve or reject
|
||||
// @Tags Revision
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param data body schema.RevisionAuditReq true "audit"
|
||||
// @Success 200 {object} handler.RespBody{}
|
||||
// @Router /answer/api/v1/revisions/audit [put]
|
||||
func (rc *RevisionController) RevisionAudit(ctx *gin.Context) {
|
||||
req := &schema.RevisionAuditReq{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
canList, err := rc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.QuestionAuditRank,
|
||||
rank.AnswerAuditRank,
|
||||
rank.TagAuditRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanReviewQuestion = canList[0]
|
||||
req.CanReviewAnswer = canList[1]
|
||||
req.CanReviewTag = canList[2]
|
||||
|
||||
err = rc.revisionListService.RevisionAudit(ctx, req)
|
||||
handler.HandleResponse(ctx, err, gin.H{})
|
||||
}
|
||||
|
||||
// CheckCanUpdateRevision check can update revision
|
||||
// @Summary check can update revision
|
||||
// @Description check can update revision
|
||||
// @Tags Revision
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security ApiKeyAuth
|
||||
// @Param id query string true "id" default(string)
|
||||
// @Success 200 {object} handler.RespBody
|
||||
// @Router /answer/api/v1/revisions/edit/check [get]
|
||||
func (rc *RevisionController) CheckCanUpdateRevision(ctx *gin.Context) {
|
||||
req := &schema.CheckCanQuestionUpdate{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
|
||||
action := ""
|
||||
objectTypeStr, _ := obj.GetObjectTypeStrByObjectID(req.ID)
|
||||
switch objectTypeStr {
|
||||
case constant.QuestionObjectType:
|
||||
action = rank.QuestionEditRank
|
||||
case constant.AnswerObjectType:
|
||||
action = rank.AnswerEditRank
|
||||
case constant.TagObjectType:
|
||||
action = rank.TagEditRank
|
||||
default:
|
||||
handler.HandleResponse(ctx, errors.BadRequest(reason.ObjectNotFound), nil)
|
||||
return
|
||||
}
|
||||
|
||||
can, err := rc.rankService.CheckOperationPermission(ctx, req.UserID, action, req.ID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err = rc.revisionListService.CheckCanUpdateRevision(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
|
|
@ -42,8 +42,7 @@ func (tc *TagController) SearchTagLike(ctx *gin.Context) {
|
|||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
userinfo := middleware.GetUserInfoFromContext(ctx)
|
||||
req.IsAdmin = userinfo.IsAdmin
|
||||
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
|
||||
resp, err := tc.tagCommonService.SearchTagLike(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
@ -64,12 +63,17 @@ func (tc *TagController) RemoveTag(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := tc.rankService.CheckRankPermission(ctx, req.UserID, rank.TagDeleteRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
can, err := tc.rankService.CheckOperationPermission(ctx, req.UserID, rank.TagDeleteRank, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := tc.tagService.RemoveTag(ctx, req.TagID)
|
||||
err = tc.tagService.RemoveTag(ctx, req.TagID)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
@ -89,13 +93,26 @@ func (tc *TagController) UpdateTag(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := tc.rankService.CheckRankPermission(ctx, req.UserID, rank.TagEditRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
canList, err := tc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.TagEditRank,
|
||||
rank.TagEditWithoutReviewRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !canList[0] {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
req.NoNeedReview = canList[1]
|
||||
|
||||
err := tc.tagService.UpdateTag(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
err = tc.tagService.UpdateTag(ctx, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
} else {
|
||||
handler.HandleResponse(ctx, err, &schema.UpdateTagResp{WaitForReview: !req.NoNeedReview})
|
||||
}
|
||||
}
|
||||
|
||||
// GetTagInfo get tag one
|
||||
|
@ -115,6 +132,16 @@ func (tc *TagController) GetTagInfo(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
canList, err := tc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.TagEditRank,
|
||||
rank.TagDeleteRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanEdit = canList[0]
|
||||
req.CanDelete = canList[1]
|
||||
|
||||
resp, err := tc.tagService.GetTagInfo(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
|
@ -163,7 +190,7 @@ func (tc *TagController) GetFollowingTags(ctx *gin.Context) {
|
|||
// @Tags Tag
|
||||
// @Produce json
|
||||
// @Param tag_id query int true "tag id"
|
||||
// @Success 200 {object} handler.RespBody{data=[]schema.GetTagSynonymsResp}
|
||||
// @Success 200 {object} handler.RespBody{data=schema.GetTagSynonymsResp}
|
||||
// @Router /answer/api/v1/tag/synonyms [get]
|
||||
func (tc *TagController) GetTagSynonyms(ctx *gin.Context) {
|
||||
req := &schema.GetTagSynonymsReq{}
|
||||
|
@ -171,6 +198,16 @@ func (tc *TagController) GetTagSynonyms(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
canList, err := tc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||
rank.TagSynonymRank,
|
||||
}, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
req.CanEdit = canList[0]
|
||||
|
||||
resp, err := tc.tagService.GetTagSynonyms(ctx, req)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
@ -191,11 +228,16 @@ func (tc *TagController) UpdateTagSynonym(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
if can, err := tc.rankService.CheckRankPermission(ctx, req.UserID, rank.TagSynonymRank); err != nil || !can {
|
||||
handler.HandleResponse(ctx, err, errors.Forbidden(reason.RankFailToMeetTheCondition))
|
||||
can, err := tc.rankService.CheckOperationPermission(ctx, req.UserID, rank.TagSynonymRank, "")
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := tc.tagService.UpdateTagSynonym(ctx, req)
|
||||
err = tc.tagService.UpdateTagSynonym(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
|
|
@ -3,20 +3,24 @@ package controller
|
|||
import (
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
"github.com/answerdev/answer/internal/base/middleware"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service"
|
||||
"github.com/answerdev/answer/internal/service/rank"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// VoteController activity controller
|
||||
type VoteController struct {
|
||||
VoteService *service.VoteService
|
||||
rankService *rank.RankService
|
||||
}
|
||||
|
||||
// NewVoteController new controller
|
||||
func NewVoteController(voteService *service.VoteService) *VoteController {
|
||||
return &VoteController{VoteService: voteService}
|
||||
func NewVoteController(voteService *service.VoteService, rankService *rank.RankService) *VoteController {
|
||||
return &VoteController{VoteService: voteService, rankService: rankService}
|
||||
}
|
||||
|
||||
// VoteUp godoc
|
||||
|
@ -34,9 +38,19 @@ func (vc *VoteController) VoteUp(ctx *gin.Context) {
|
|||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
can, err := vc.rankService.CheckVotePermission(ctx, req.UserID, req.ObjectID, true)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
dto := &schema.VoteDTO{}
|
||||
_ = copier.Copy(dto, req)
|
||||
dto.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
resp, err := vc.VoteService.VoteUp(ctx, dto)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, schema.ErrTypeToast)
|
||||
|
@ -60,10 +74,20 @@ func (vc *VoteController) VoteDown(ctx *gin.Context) {
|
|||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
can, err := vc.rankService.CheckVotePermission(ctx, req.UserID, req.ObjectID, false)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
dto := &schema.VoteDTO{}
|
||||
_ = copier.Copy(dto, req)
|
||||
|
||||
dto.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
resp, err := vc.VoteService.VoteDown(ctx, dto)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, schema.ErrTypeToast)
|
||||
|
|
|
@ -9,16 +9,19 @@ const (
|
|||
|
||||
// Activity activity
|
||||
type Activity struct {
|
||||
ID string `xorm:"not null pk autoincr BIGINT(20) id"`
|
||||
CreatedAt time.Time `xorm:"created TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"updated TIMESTAMP updated_at"`
|
||||
UserID string `xorm:"not null index BIGINT(20) user_id"`
|
||||
TriggerUserID int64 `xorm:"not null default 0 index BIGINT(20) trigger_user_id"`
|
||||
ObjectID string `xorm:"not null default 0 index BIGINT(20) object_id"`
|
||||
ActivityType int `xorm:"not null INT(11) activity_type"`
|
||||
Cancelled int `xorm:"not null default 0 TINYINT(4) cancelled"`
|
||||
Rank int `xorm:"not null default 0 INT(11) rank"`
|
||||
HasRank int `xorm:"not null default 0 TINYINT(4) has_rank"`
|
||||
ID string `xorm:"not null pk autoincr BIGINT(20) id"`
|
||||
CreatedAt time.Time `xorm:"created TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"updated TIMESTAMP updated_at"`
|
||||
CancelledAt time.Time `xorm:"TIMESTAMP cancelled_at"`
|
||||
UserID string `xorm:"not null index BIGINT(20) user_id"`
|
||||
TriggerUserID int64 `xorm:"not null default 0 index BIGINT(20) trigger_user_id"`
|
||||
ObjectID string `xorm:"not null default 0 index BIGINT(20) object_id"`
|
||||
OriginalObjectID string `xorm:"not null default 0 BIGINT(20) original_object_id"`
|
||||
ActivityType int `xorm:"not null INT(11) activity_type"`
|
||||
Cancelled int `xorm:"not null default 0 TINYINT(4) cancelled"`
|
||||
Rank int `xorm:"not null default 0 INT(11) rank"`
|
||||
HasRank int `xorm:"not null default 0 TINYINT(4) has_rank"`
|
||||
RevisionID int64 `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
}
|
||||
|
||||
type ActivityRankSum struct {
|
||||
|
|
|
@ -18,18 +18,19 @@ var CmsAnswerSearchStatus = map[string]int{
|
|||
|
||||
// Answer answer
|
||||
type Answer struct {
|
||||
ID string `xorm:"not null pk autoincr BIGINT(20) id"`
|
||||
CreatedAt time.Time `xorm:"created not null default CURRENT_TIMESTAMP TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"not null default CURRENT_TIMESTAMP TIMESTAMP updated_at"`
|
||||
QuestionID string `xorm:"not null default 0 BIGINT(20) question_id"`
|
||||
UserID string `xorm:"not null default 0 BIGINT(20) INDEX user_id"`
|
||||
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"`
|
||||
Adopted int `xorm:"not null default 1 INT(11) adopted"`
|
||||
CommentCount int `xorm:"not null default 0 INT(11) comment_count"`
|
||||
VoteCount int `xorm:"not null default 0 INT(11) vote_count"`
|
||||
RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
ID string `xorm:"not null pk autoincr BIGINT(20) id"`
|
||||
CreatedAt time.Time `xorm:"created not null default CURRENT_TIMESTAMP TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"updated_at TIMESTAMP"`
|
||||
QuestionID string `xorm:"not null default 0 BIGINT(20) question_id"`
|
||||
UserID string `xorm:"not null default 0 BIGINT(20) INDEX user_id"`
|
||||
LastEditUserID string `xorm:"not null default 0 BIGINT(20) last_edit_user_id"`
|
||||
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"`
|
||||
Adopted int `xorm:"not null default 1 INT(11) adopted"`
|
||||
CommentCount int `xorm:"not null default 0 INT(11) comment_count"`
|
||||
VoteCount int `xorm:"not null default 0 INT(11) vote_count"`
|
||||
RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
}
|
||||
|
||||
type AnswerSearch struct {
|
||||
|
|
|
@ -6,19 +6,19 @@ import (
|
|||
|
||||
const (
|
||||
QuestionStatusAvailable = 1
|
||||
QuestionStatusclosed = 2
|
||||
QuestionStatusClosed = 2
|
||||
QuestionStatusDeleted = 10
|
||||
)
|
||||
|
||||
var CmsQuestionSearchStatus = map[string]int{
|
||||
"available": QuestionStatusAvailable,
|
||||
"closed": QuestionStatusclosed,
|
||||
"closed": QuestionStatusClosed,
|
||||
"deleted": QuestionStatusDeleted,
|
||||
}
|
||||
|
||||
var CmsQuestionSearchStatusIntToString = map[int]string{
|
||||
QuestionStatusAvailable: "available",
|
||||
QuestionStatusclosed: "closed",
|
||||
QuestionStatusClosed: "closed",
|
||||
QuestionStatusDeleted: "deleted",
|
||||
}
|
||||
|
||||
|
@ -31,8 +31,9 @@ type QuestionTag struct {
|
|||
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:"not null default CURRENT_TIMESTAMP TIMESTAMP updated_at"`
|
||||
UpdatedAt time.Time `xorm:"updated_at TIMESTAMP"`
|
||||
UserID string `xorm:"not null default 0 BIGINT(20) INDEX 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"`
|
||||
|
@ -45,11 +46,28 @@ type Question struct {
|
|||
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:"default CURRENT_TIMESTAMP TIMESTAMP post_update_time"`
|
||||
RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
PostUpdateTime time.Time `xorm:"post_update_time TIMESTAMP"`
|
||||
RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
}
|
||||
|
||||
// TableName question table name
|
||||
func (Question) TableName() string {
|
||||
return "question"
|
||||
}
|
||||
|
||||
// QuestionWithTagsRevision question
|
||||
type QuestionWithTagsRevision struct {
|
||||
Question
|
||||
Tags []*TagSimpleInfoForRevision `json:"tags"`
|
||||
}
|
||||
|
||||
// TagSimpleInfoForRevision tag simple info for revision
|
||||
type TagSimpleInfoForRevision struct {
|
||||
ID string `xorm:"not null pk comment('tag_id') BIGINT(20) id"`
|
||||
MainTagID int64 `xorm:"not null default 0 BIGINT(20) main_tag_id"`
|
||||
SlugName string `xorm:"not null default '' unique VARCHAR(35) slug_name"`
|
||||
DisplayName string `xorm:"not null default '' VARCHAR(35) display_name"`
|
||||
Recommend bool `xorm:"not null default false BOOL recommend"`
|
||||
Reserved bool `xorm:"not null default false BOOL reserved"`
|
||||
RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
}
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
package entity
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// RevisionUnreviewedStatus this revision is unreviewed
|
||||
RevisionUnreviewedStatus = 1
|
||||
// RevisionReviewPassStatus this revision is reviewed and approved by operator
|
||||
RevisionReviewPassStatus = 2
|
||||
// RevisionReviewRejectStatus this revision is reviewed and rejected by operator
|
||||
RevisionReviewRejectStatus = 3
|
||||
)
|
||||
|
||||
// Revision revision
|
||||
type Revision struct {
|
||||
ID string `xorm:"not null pk autoincr BIGINT(20) id"`
|
||||
CreatedAt time.Time `xorm:"created TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"updated TIMESTAMP updated_at"`
|
||||
UserID string `xorm:"not null default 0 BIGINT(20) user_id"`
|
||||
ObjectType int `xorm:"not null default 0 ) INT(11) object_type"`
|
||||
ObjectID string `xorm:"not null default 0 BIGINT(20) INDEX object_id"`
|
||||
Title string `xorm:"not null default '' VARCHAR(255) title"`
|
||||
Content string `xorm:"not null TEXT content"`
|
||||
Log string `xorm:"VARCHAR(255) log"`
|
||||
// Status todo: this field is not used, will be removed in the future
|
||||
Status int `xorm:"not null default 1 INT(11) status"`
|
||||
ID string `xorm:"not null pk autoincr BIGINT(20) id"`
|
||||
CreatedAt time.Time `xorm:"created TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"updated TIMESTAMP updated_at"`
|
||||
UserID string `xorm:"not null default 0 BIGINT(20) user_id"`
|
||||
ObjectType int `xorm:"not null default 0 INT(11) object_type"`
|
||||
ObjectID string `xorm:"not null default 0 BIGINT(20) INDEX object_id"`
|
||||
Title string `xorm:"not null default '' VARCHAR(255) title"`
|
||||
Content string `xorm:"not null TEXT content"`
|
||||
Log string `xorm:"VARCHAR(255) log"`
|
||||
Status int `xorm:"not null default 1 INT(11) status"`
|
||||
ReviewUserID int64 `xorm:"not null default 0 BIGINT(20) review_user_id"`
|
||||
}
|
||||
|
||||
// TableName revision table name
|
||||
|
|
|
@ -24,6 +24,7 @@ type Tag struct {
|
|||
Recommend bool `xorm:"not null default false BOOL recommend"`
|
||||
Reserved bool `xorm:"not null default false BOOL reserved"`
|
||||
RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
UserID string `xorm:"not null default 0 BIGINT(20) user_id"`
|
||||
}
|
||||
|
||||
// TableName tag table name
|
||||
|
|
|
@ -163,7 +163,7 @@ func InitBaseInfo(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
if cli.CheckDBTableExist(c.Data.Database) {
|
||||
log.Warnf("database is already initialized")
|
||||
log.Warn("database is already initialized")
|
||||
handler.HandleResponse(ctx, nil, nil)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -246,6 +246,26 @@ func initConfigTable(engine *xorm.Engine) error {
|
|||
{ID: 84, Key: "question.review.reasons", Value: `["reason.looks_ok","reason.needs_edit","reason.needs_close","reason.needs_delete"]`},
|
||||
{ID: 85, Key: "answer.review.reasons", Value: `["reason.looks_ok","reason.needs_edit","reason.needs_delete"]`},
|
||||
{ID: 86, Key: "comment.review.reasons", Value: `["reason.looks_ok","reason.needs_edit","reason.needs_delete"]`},
|
||||
{ID: 87, Key: "question.asked", Value: `0`},
|
||||
{ID: 88, Key: "question.closed", Value: `0`},
|
||||
{ID: 89, Key: "question.reopened", Value: `0`},
|
||||
{ID: 90, Key: "question.answered", Value: `0`},
|
||||
{ID: 91, Key: "question.commented", Value: `0`},
|
||||
{ID: 92, Key: "question.accept", Value: `0`},
|
||||
{ID: 93, Key: "question.edited", Value: `0`},
|
||||
{ID: 94, Key: "question.rollback", Value: `0`},
|
||||
{ID: 95, Key: "question.deleted", Value: `0`},
|
||||
{ID: 96, Key: "question.undeleted", Value: `0`},
|
||||
{ID: 97, Key: "answer.answered", Value: `0`},
|
||||
{ID: 98, Key: "answer.commented", Value: `0`},
|
||||
{ID: 99, Key: "answer.edited", Value: `0`},
|
||||
{ID: 100, Key: "answer.rollback", Value: `0`},
|
||||
{ID: 101, Key: "answer.undeleted", Value: `0`},
|
||||
{ID: 102, Key: "tag.created", Value: `0`},
|
||||
{ID: 103, Key: "tag.edited", Value: `0`},
|
||||
{ID: 104, Key: "tag.rollback", Value: `0`},
|
||||
{ID: 105, Key: "tag.deleted", Value: `0`},
|
||||
{ID: 106, Key: "tag.undeleted", Value: `0`},
|
||||
}
|
||||
_, err := engine.Insert(defaultConfigTable)
|
||||
return err
|
||||
|
|
|
@ -44,6 +44,7 @@ var migrations = []Migration{
|
|||
NewMigration("this is first version, no operation", noopMigration),
|
||||
NewMigration("add user language", addUserLanguage),
|
||||
NewMigration("add recommend and reserved tag fields", addTagRecommendedAndReserved),
|
||||
NewMigration("add activity timeline", addActivityTimeline),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addActivityTimeline(x *xorm.Engine) error {
|
||||
type Revision struct {
|
||||
ReviewUserID int64 `xorm:"not null default 0 BIGINT(20) review_user_id"`
|
||||
}
|
||||
type Activity struct {
|
||||
CancelledAt time.Time `xorm:"TIMESTAMP cancelled_at"`
|
||||
RevisionID int64 `xorm:"not null default 0 BIGINT(20) revision_id"`
|
||||
OriginalObjectID string `xorm:"not null default 0 BIGINT(20) original_object_id"`
|
||||
}
|
||||
return x.Sync(new(Activity), new(Revision))
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package activity
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/service/activity"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// activityRepo activity repository
|
||||
type activityRepo struct {
|
||||
data *data.Data
|
||||
}
|
||||
|
||||
// NewActivityRepo new repository
|
||||
func NewActivityRepo(
|
||||
data *data.Data,
|
||||
) activity.ActivityRepo {
|
||||
return &activityRepo{
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (ar *activityRepo) GetObjectAllActivity(ctx context.Context, objectID string, showVote bool) (
|
||||
activityList []*entity.Activity, err error) {
|
||||
activityList = make([]*entity.Activity, 0)
|
||||
err = ar.data.DB.Find(&activityList, &entity.Activity{OriginalObjectID: objectID})
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return activityList, nil
|
||||
}
|
|
@ -2,6 +2,7 @@ package activity
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
|
@ -96,8 +97,8 @@ func (ar *AnswerActivityRepo) DeleteQuestion(ctx context.Context, questionID str
|
|||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
|
||||
if _, e := session.Where("id = ?", act.ID).Cols("`cancelled`").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityCancelled}); e != nil {
|
||||
if _, e := session.Where("id = ?", act.ID).Cols("cancelled", "cancelled_at").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityCancelled, CancelledAt: time.Now()}); e != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +125,7 @@ func (ar *AnswerActivityRepo) DeleteQuestion(ctx context.Context, questionID str
|
|||
|
||||
// AcceptAnswer accept other answer
|
||||
func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
|
||||
answerObjID, questionUserID, answerUserID string, isSelf bool,
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool,
|
||||
) (err error) {
|
||||
addActivityList := make([]*entity.Activity, 0)
|
||||
for _, action := range acceptActionList {
|
||||
|
@ -134,10 +135,11 @@ func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
|
|||
return errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
addActivity := &entity.Activity{
|
||||
ObjectID: answerObjID,
|
||||
ActivityType: activityType,
|
||||
Rank: deltaRank,
|
||||
HasRank: hasRank,
|
||||
ObjectID: answerObjID,
|
||||
OriginalObjectID: questionObjID,
|
||||
ActivityType: activityType,
|
||||
Rank: deltaRank,
|
||||
HasRank: hasRank,
|
||||
}
|
||||
if action == acceptAction {
|
||||
addActivity.UserID = questionUserID
|
||||
|
@ -222,7 +224,7 @@ func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
|
|||
|
||||
// CancelAcceptAnswer accept other answer
|
||||
func (ar *AnswerActivityRepo) CancelAcceptAnswer(ctx context.Context,
|
||||
answerObjID, questionUserID, answerUserID string,
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string,
|
||||
) (err error) {
|
||||
addActivityList := make([]*entity.Activity, 0)
|
||||
for _, action := range acceptActionList {
|
||||
|
@ -232,10 +234,11 @@ func (ar *AnswerActivityRepo) CancelAcceptAnswer(ctx context.Context,
|
|||
return errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
addActivity := &entity.Activity{
|
||||
ObjectID: answerObjID,
|
||||
ActivityType: activityType,
|
||||
Rank: -deltaRank,
|
||||
HasRank: hasRank,
|
||||
ObjectID: answerObjID,
|
||||
OriginalObjectID: questionObjID,
|
||||
ActivityType: activityType,
|
||||
Rank: -deltaRank,
|
||||
HasRank: hasRank,
|
||||
}
|
||||
if action == acceptAction {
|
||||
addActivity.UserID = questionUserID
|
||||
|
@ -265,8 +268,8 @@ func (ar *AnswerActivityRepo) CancelAcceptAnswer(ctx context.Context,
|
|||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
|
||||
if _, e := session.Where("id = ?", existsActivity.ID).Cols("`cancelled`").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityCancelled}); e != nil {
|
||||
if _, e := session.Where("id = ?", existsActivity.ID).Cols("cancelled", "cancelled_at").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityCancelled, CancelledAt: time.Now()}); e != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
}
|
||||
|
@ -326,8 +329,8 @@ func (ar *AnswerActivityRepo) DeleteAnswer(ctx context.Context, answerID string)
|
|||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
|
||||
if _, e := session.Where("id = ?", act.ID).Cols("`cancelled`").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityCancelled}); e != nil {
|
||||
if _, e := session.Where("id = ?", act.ID).Cols("cancelled", "cancelled_at").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityCancelled, CancelledAt: time.Now()}); e != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package activity
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/follow"
|
||||
|
@ -59,7 +60,7 @@ func (ar *FollowRepo) Follow(ctx context.Context, objectID, userID string) error
|
|||
return
|
||||
}
|
||||
|
||||
if has && existsActivity.Cancelled == 0 {
|
||||
if has && existsActivity.Cancelled == entity.ActivityAvailable {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -67,17 +68,18 @@ func (ar *FollowRepo) Follow(ctx context.Context, objectID, userID string) error
|
|||
_, err = session.Where(builder.Eq{"id": existsActivity.ID}).
|
||||
Cols(`cancelled`).
|
||||
Update(&entity.Activity{
|
||||
Cancelled: 0,
|
||||
Cancelled: entity.ActivityAvailable,
|
||||
})
|
||||
} else {
|
||||
// update existing activity with new user id and u object id
|
||||
_, err = session.Insert(&entity.Activity{
|
||||
UserID: userID,
|
||||
ObjectID: objectID,
|
||||
ActivityType: activityType,
|
||||
Cancelled: 0,
|
||||
Rank: 0,
|
||||
HasRank: 0,
|
||||
UserID: userID,
|
||||
ObjectID: objectID,
|
||||
OriginalObjectID: objectID,
|
||||
ActivityType: activityType,
|
||||
Cancelled: entity.ActivityAvailable,
|
||||
Rank: 0,
|
||||
HasRank: 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -120,13 +122,14 @@ func (ar *FollowRepo) FollowCancel(ctx context.Context, objectID, userID string)
|
|||
return
|
||||
}
|
||||
|
||||
if has && existsActivity.Cancelled == 1 {
|
||||
if has && existsActivity.Cancelled == entity.ActivityCancelled {
|
||||
return
|
||||
}
|
||||
if _, err = session.Where("id = ?", existsActivity.ID).
|
||||
Cols("cancelled").
|
||||
Update(&entity.Activity{
|
||||
Cancelled: 1,
|
||||
Cancelled: entity.ActivityCancelled,
|
||||
CancelledAt: time.Now(),
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -55,11 +55,12 @@ func (ar *UserActiveActivityRepo) UserActive(ctx context.Context, userID string)
|
|||
}
|
||||
|
||||
addActivity := &entity.Activity{
|
||||
UserID: userID,
|
||||
ObjectID: "0",
|
||||
ActivityType: activityType,
|
||||
Rank: deltaRank,
|
||||
HasRank: 1,
|
||||
UserID: userID,
|
||||
ObjectID: "0",
|
||||
OriginalObjectID: "0",
|
||||
ActivityType: activityType,
|
||||
Rank: deltaRank,
|
||||
HasRank: 1,
|
||||
}
|
||||
_, exists, err := ar.activityRepo.GetActivity(ctx, session, "0", addActivity.UserID, activityType)
|
||||
if err != nil {
|
||||
|
|
|
@ -3,6 +3,7 @@ package activity
|
|||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/pkg/converter"
|
||||
|
||||
|
@ -100,18 +101,19 @@ func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUse
|
|||
Get(&existsActivity)
|
||||
|
||||
// is is voted,return
|
||||
if has && existsActivity.Cancelled == 0 {
|
||||
if has && existsActivity.Cancelled == entity.ActivityAvailable {
|
||||
return
|
||||
}
|
||||
|
||||
insertActivity = entity.Activity{
|
||||
ObjectID: objectID,
|
||||
UserID: activityUserID,
|
||||
TriggerUserID: converter.StringToInt64(triggerUserID),
|
||||
ActivityType: activityType,
|
||||
Rank: deltaRank,
|
||||
HasRank: hasRank,
|
||||
Cancelled: 0,
|
||||
ObjectID: objectID,
|
||||
OriginalObjectID: objectID,
|
||||
UserID: activityUserID,
|
||||
TriggerUserID: converter.StringToInt64(triggerUserID),
|
||||
ActivityType: activityType,
|
||||
Rank: deltaRank,
|
||||
HasRank: hasRank,
|
||||
Cancelled: entity.ActivityAvailable,
|
||||
}
|
||||
|
||||
// trigger user rank and send notification
|
||||
|
@ -131,7 +133,7 @@ func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUse
|
|||
if has {
|
||||
if _, err = session.Where("id = ?", existsActivity.ID).Cols("`cancelled`").
|
||||
Update(&entity.Activity{
|
||||
Cancelled: 0,
|
||||
Cancelled: entity.ActivityAvailable,
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -201,13 +203,14 @@ func (vr *VoteRepo) voteCancel(ctx context.Context, objectID string, userID, obj
|
|||
return
|
||||
}
|
||||
|
||||
if existsActivity.Cancelled == 1 {
|
||||
if existsActivity.Cancelled == entity.ActivityCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = session.Where("id = ?", existsActivity.ID).Cols("`cancelled`").
|
||||
if _, err = session.Where("id = ?", existsActivity.ID).Cols("cancelled", "cancelled_at").
|
||||
Update(&entity.Activity{
|
||||
Cancelled: 1,
|
||||
Cancelled: entity.ActivityCancelled,
|
||||
CancelledAt: time.Now(),
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -63,6 +63,14 @@ func (ar *ActivityRepo) GetActivityTypeByObjKey(ctx context.Context, objectKey,
|
|||
return
|
||||
}
|
||||
|
||||
func (ar *ActivityRepo) GetActivityTypeByConfigKey(ctx context.Context, configKey string) (activityType int, err error) {
|
||||
activityType, err = ar.configRepo.GetConfigType(configKey)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ar *ActivityRepo) GetActivity(ctx context.Context, session *xorm.Session,
|
||||
objectID, userID string, activityType int,
|
||||
) (existsActivity *entity.Activity, exist bool, err error) {
|
||||
|
@ -89,3 +97,12 @@ func (ar *ActivityRepo) GetUserIDObjectIDActivitySum(ctx context.Context, userID
|
|||
}
|
||||
return sum.Rank, nil
|
||||
}
|
||||
|
||||
// AddActivity add activity
|
||||
func (ar *ActivityRepo) AddActivity(ctx context.Context, activity *entity.Activity) (err error) {
|
||||
_, err = ar.data.DB.Insert(activity)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ func (ar *FollowRepo) IsFollowed(userID, objectID string) (bool, error) {
|
|||
if !has {
|
||||
return false, nil
|
||||
}
|
||||
if at.Cancelled == 1 {
|
||||
if at.Cancelled == entity.ActivityCancelled {
|
||||
return false, nil
|
||||
} else {
|
||||
return true, nil
|
||||
|
|
|
@ -54,6 +54,7 @@ var ProviderSetRepo = wire.NewSet(
|
|||
activity.NewAnswerActivityRepo,
|
||||
activity.NewQuestionActivityRepo,
|
||||
activity.NewUserActiveActivityRepo,
|
||||
activity.NewActivityRepo,
|
||||
tag.NewTagRepo,
|
||||
tag_common.NewTagCommonRepo,
|
||||
tag.NewTagRelRepo,
|
||||
|
|
|
@ -166,7 +166,7 @@ func (qr *questionRepo) GetQuestionList(ctx context.Context, question *entity.Qu
|
|||
func (qr *questionRepo) GetQuestionCount(ctx context.Context) (count int64, err error) {
|
||||
questionList := make([]*entity.Question, 0)
|
||||
|
||||
count, err = qr.data.DB.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusclosed}).FindAndCount(&questionList)
|
||||
count, err = qr.data.DB.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusClosed}).FindAndCount(&questionList)
|
||||
if err != nil {
|
||||
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ func (qr *questionRepo) SearchList(ctx context.Context, search *schema.QuestionS
|
|||
session = session.And("question.user_id = ?", search.UserID)
|
||||
}
|
||||
|
||||
session = session.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusclosed})
|
||||
session = session.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusClosed})
|
||||
// if search.Status > 0 {
|
||||
// session = session.And("question.status = ?", search.Status)
|
||||
// }
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/base/pager"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/service/revision"
|
||||
|
@ -79,6 +80,21 @@ func (rr *revisionRepo) UpdateObjectRevisionId(ctx context.Context, revision *en
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateStatus update revision status
|
||||
func (rr *revisionRepo) UpdateStatus(ctx context.Context, id string, status int) (err error) {
|
||||
if id == "" {
|
||||
return nil
|
||||
}
|
||||
var data entity.Revision
|
||||
data.ID = id
|
||||
data.Status = status
|
||||
_, err = rr.data.DB.Where("id =?", id).Cols("status").Update(&data)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRevision get revision one
|
||||
func (rr *revisionRepo) GetRevision(ctx context.Context, id string) (
|
||||
revision *entity.Revision, exist bool, err error,
|
||||
|
@ -91,6 +107,27 @@ func (rr *revisionRepo) GetRevision(ctx context.Context, id string) (
|
|||
return
|
||||
}
|
||||
|
||||
// GetRevisionByID get object's last revision by object TagID
|
||||
func (rr *revisionRepo) GetRevisionByID(ctx context.Context, revisionID string) (
|
||||
revision *entity.Revision, exist bool, err error) {
|
||||
revision = &entity.Revision{}
|
||||
exist, err = rr.data.DB.Where("id = ?", revisionID).Get(revision)
|
||||
if err != nil {
|
||||
return nil, false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rr *revisionRepo) ExistUnreviewedByObjectID(ctx context.Context, objectID string) (
|
||||
revision *entity.Revision, exist bool, err error) {
|
||||
revision = &entity.Revision{}
|
||||
exist, err = rr.data.DB.Where("object_id = ?", objectID).And("status = ?", entity.RevisionUnreviewedStatus).Get(revision)
|
||||
if err != nil {
|
||||
return nil, false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetLastRevisionByObjectID get object's last revision by object TagID
|
||||
func (rr *revisionRepo) GetLastRevisionByObjectID(ctx context.Context, objectID string) (
|
||||
revision *entity.Revision, exist bool, err error,
|
||||
|
@ -128,3 +165,22 @@ func (rr *revisionRepo) allowRecord(objectType int) (ok bool) {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// GetUnreviewedRevisionPage get unreviewed revision page
|
||||
func (rr *revisionRepo) GetUnreviewedRevisionPage(ctx context.Context, page int, pageSize int,
|
||||
objectTypeList []int) (revisionList []*entity.Revision, total int64, err error) {
|
||||
revisionList = make([]*entity.Revision, 0)
|
||||
if len(objectTypeList) == 0 {
|
||||
return revisionList, 0, nil
|
||||
}
|
||||
session := rr.data.DB.NewSession()
|
||||
session = session.And("status = ?", entity.RevisionUnreviewedStatus)
|
||||
session = session.In("object_type", objectTypeList)
|
||||
session = session.OrderBy("created_at desc")
|
||||
|
||||
total, err = pager.Help(page, pageSize, &revisionList, &entity.Revision{}, session)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -471,20 +471,6 @@ func (sr *searchRepo) parseResult(ctx context.Context, res []map[string][]byte)
|
|||
return
|
||||
}
|
||||
|
||||
// userBasicInfoFormat
|
||||
func (sr *searchRepo) userBasicInfoFormat(ctx context.Context, dbinfo *entity.User) *schema.UserBasicInfo {
|
||||
return &schema.UserBasicInfo{
|
||||
ID: dbinfo.ID,
|
||||
Username: dbinfo.Username,
|
||||
Rank: dbinfo.Rank,
|
||||
DisplayName: dbinfo.DisplayName,
|
||||
Avatar: dbinfo.Avatar,
|
||||
Website: dbinfo.Website,
|
||||
Location: dbinfo.Location,
|
||||
IPInfo: dbinfo.IPInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func addRelevanceField(searchFields, words, fields []string) (res []string, args []interface{}) {
|
||||
relevanceRes := []string{}
|
||||
args = []interface{}{}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/service/tag"
|
||||
"github.com/answerdev/answer/internal/service/tag_common"
|
||||
"github.com/answerdev/answer/internal/service/unique"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"xorm.io/builder"
|
||||
|
@ -22,7 +22,7 @@ type tagRepo struct {
|
|||
func NewTagRepo(
|
||||
data *data.Data,
|
||||
uniqueIDRepo unique.UniqueIDRepo,
|
||||
) tag.TagRepo {
|
||||
) tag_common.TagRepo {
|
||||
return &tagRepo{
|
||||
data: data,
|
||||
uniqueIDRepo: uniqueIDRepo,
|
||||
|
|
|
@ -28,7 +28,7 @@ func NewUserRepo(data *data.Data, configRepo config.ConfigRepo) usercommon.UserR
|
|||
|
||||
// AddUser add user
|
||||
func (ur *userRepo) AddUser(ctx context.Context, user *entity.User) (err error) {
|
||||
_, err = ur.data.DB.Insert(user)
|
||||
_, err = ur.data.DB.UseBool("is_admin").Insert(user)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ type AnswerAPIRouter struct {
|
|||
notificationController *controller.NotificationController
|
||||
dashboardController *controller.DashboardController
|
||||
uploadController *controller.UploadController
|
||||
activityController *controller.ActivityController
|
||||
roleController *controller_backyard.RoleController
|
||||
}
|
||||
|
||||
|
@ -55,6 +56,7 @@ func NewAnswerAPIRouter(
|
|||
notificationController *controller.NotificationController,
|
||||
dashboardController *controller.DashboardController,
|
||||
uploadController *controller.UploadController,
|
||||
activityController *controller.ActivityController,
|
||||
roleController *controller_backyard.RoleController,
|
||||
) *AnswerAPIRouter {
|
||||
return &AnswerAPIRouter{
|
||||
|
@ -80,6 +82,7 @@ func NewAnswerAPIRouter(
|
|||
siteinfoController: siteinfoController,
|
||||
dashboardController: dashboardController,
|
||||
uploadController: uploadController,
|
||||
activityController: activityController,
|
||||
roleController: roleController,
|
||||
}
|
||||
}
|
||||
|
@ -140,9 +143,15 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
|
|||
//siteinfo
|
||||
r.GET("/siteinfo", a.siteinfoController.GetSiteInfo)
|
||||
r.GET("/siteinfo/legal", a.siteinfoController.GetSiteLegalInfo)
|
||||
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
|
||||
//revisions
|
||||
r.GET("/revisions/unreviewed", a.revisionController.GetUnreviewedRevisionList)
|
||||
r.PUT("/revisions/audit", a.revisionController.RevisionAudit)
|
||||
r.GET("/revisions/edit/check", a.revisionController.CheckCanUpdateRevision)
|
||||
|
||||
// comment
|
||||
r.POST("/comment", a.commentController.AddComment)
|
||||
r.DELETE("/comment", a.commentController.RemoveComment)
|
||||
|
@ -203,6 +212,11 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
|
|||
|
||||
// upload file
|
||||
r.POST("/file", a.uploadController.UploadFile)
|
||||
|
||||
// activity
|
||||
r.GET("/activity/timeline", a.activityController.GetObjectTimeline)
|
||||
r.GET("/activity/timeline/detail", a.activityController.GetObjectTimelineDetail)
|
||||
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterAnswerCmsAPIRouter(r *gin.RouterGroup) {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package schema
|
||||
|
||||
import "github.com/answerdev/answer/internal/base/constant"
|
||||
|
||||
// ActivityMsg activity message
|
||||
type ActivityMsg struct {
|
||||
UserID string `json:"user_id"`
|
||||
TriggerUserID int64 `json:"trigger_user_id"`
|
||||
ObjectID string `json:"object_id"`
|
||||
OriginalObjectID string `json:"original_object_id"`
|
||||
ActivityTypeKey constant.ActivityTypeKey `json:"activity_type_key"`
|
||||
RevisionID string `json:"revision_id"`
|
||||
}
|
||||
|
||||
// GetObjectTimelineReq get object timeline request
|
||||
type GetObjectTimelineReq struct {
|
||||
ObjectID string `validate:"omitempty,gt=0,lte=100" form:"object_id"`
|
||||
ShowVote bool `validate:"omitempty" form:"show_vote"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// GetObjectTimelineResp get object timeline response
|
||||
type GetObjectTimelineResp struct {
|
||||
ObjectInfo *ActObjectInfo `json:"object_info"`
|
||||
Timeline []*ActObjectTimeline `json:"timeline"`
|
||||
}
|
||||
|
||||
// ActObjectTimeline act object timeline
|
||||
type ActObjectTimeline struct {
|
||||
ActivityID string `json:"activity_id"`
|
||||
RevisionID string `json:"revision_id"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
ActivityType string `json:"activity_type"`
|
||||
Username string `json:"username"`
|
||||
UserDisplayName string `json:"user_display_name"`
|
||||
Comment string `json:"comment"`
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectType string `json:"object_type"`
|
||||
Cancelled bool `json:"cancelled"`
|
||||
CancelledAt int64 `json:"cancelled_at"`
|
||||
}
|
||||
|
||||
// ActObjectInfo act object info
|
||||
type ActObjectInfo struct {
|
||||
Title string `json:"title"`
|
||||
ObjectType string `json:"object_type"`
|
||||
QuestionID string `json:"question_id"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"display_name"`
|
||||
}
|
||||
|
||||
// GetObjectTimelineDetailReq get object timeline detail request
|
||||
type GetObjectTimelineDetailReq struct {
|
||||
NewRevisionID string `validate:"required,gt=0,lte=100" form:"new_revision_id"`
|
||||
OldRevisionID string `validate:"required,gt=0,lte=100" form:"old_revision_id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// GetObjectTimelineDetailResp get object timeline detail response
|
||||
type GetObjectTimelineDetailResp struct {
|
||||
NewRevision *ObjectTimelineDetail `json:"new_revision"`
|
||||
OldRevision *ObjectTimelineDetail `json:"old_revision"`
|
||||
}
|
||||
|
||||
// ObjectTimelineDetail object timeline detail
|
||||
type ObjectTimelineDetail struct {
|
||||
Title string `json:"title"`
|
||||
Tags []string `json:"tags"`
|
||||
OriginalText string `json:"original_text"`
|
||||
}
|
|
@ -5,7 +5,8 @@ type RemoveAnswerReq struct {
|
|||
// answer id
|
||||
ID string `validate:"required" json:"id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -21,21 +22,34 @@ type AnswerAddReq struct {
|
|||
}
|
||||
|
||||
type AnswerUpdateReq struct {
|
||||
ID string `json:"id"` // id
|
||||
QuestionID string `json:"question_id" ` // question_id
|
||||
UserID string `json:"-" ` // user_id
|
||||
Title string `json:"title" ` // title
|
||||
Content string `json:"content"` // content
|
||||
HTML string `json:"html" ` // html
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"` // edit_summary
|
||||
ID string `json:"id"` // id
|
||||
QuestionID string `json:"question_id" ` // question_id
|
||||
UserID string `json:"-" ` // user_id
|
||||
Title string `json:"title" ` // title
|
||||
Content string `json:"content"` // content
|
||||
HTML string `json:"html" ` // html
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"` // edit_summary
|
||||
NoNeedReview bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
}
|
||||
|
||||
type AnswerList struct {
|
||||
QuestionID string `json:"question_id" form:"question_id"` // question_id
|
||||
Order string `json:"order" form:"order"` // 1 Default 2 time
|
||||
Page int `json:"page" form:"page"` // Query number of pages
|
||||
PageSize int `json:"page_size" form:"page_size"` // Search page size
|
||||
LoginUserID string `json:"-" `
|
||||
// AnswerUpdateResp answer update resp
|
||||
type AnswerUpdateResp struct {
|
||||
WaitForReview bool `json:"wait_for_review"`
|
||||
}
|
||||
|
||||
type AnswerListReq struct {
|
||||
QuestionID string `json:"question_id" form:"question_id"` // question_id
|
||||
Order string `json:"order" form:"order"` // 1 Default 2 time
|
||||
Page int `json:"page" form:"page"` // Query number of pages
|
||||
PageSize int `json:"page_size" form:"page_size"` // Search page size
|
||||
UserID string `json:"-" `
|
||||
IsAdmin bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
}
|
||||
|
||||
type AnswerInfo struct {
|
||||
|
@ -47,6 +61,7 @@ type AnswerInfo struct {
|
|||
UpdateTime int64 `json:"update_time" xorm:"updated"` // update_time
|
||||
Adopted int `json:"adopted"` // 1 Failed 2 Adopted
|
||||
UserID string `json:"-" `
|
||||
UpdateUserID string `json:"-" `
|
||||
UserInfo *UserBasicInfo `json:"user_info,omitempty"`
|
||||
UpdateUserInfo *UserBasicInfo `json:"update_user_info,omitempty"`
|
||||
Collected bool `json:"collected"`
|
||||
|
@ -66,6 +81,7 @@ type AdminAnswerInfo struct {
|
|||
UpdateTime int64 `json:"update_time"`
|
||||
Adopted int `json:"adopted"`
|
||||
UserID string `json:"-" `
|
||||
UpdateUserID string `json:"-" `
|
||||
UserInfo *UserBasicInfo `json:"user_info"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
QuestionInfo struct {
|
||||
|
|
|
@ -19,6 +19,12 @@ type AddCommentReq struct {
|
|||
MentionUsernameList []string `validate:"omitempty" json:"mention_username_list"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
// whether user can add it
|
||||
CanAdd bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
}
|
||||
|
||||
// RemoveCommentReq remove comment
|
||||
|
@ -73,6 +79,10 @@ type GetCommentWithPageReq struct {
|
|||
QueryCond string `validate:"omitempty,oneof=vote" form:"query_cond"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetCommentReq get comment list page request
|
||||
|
@ -81,6 +91,10 @@ type GetCommentReq struct {
|
|||
ID string `validate:"required" form:"id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetCommentResp comment response
|
||||
|
|
|
@ -27,6 +27,13 @@ type NotificationContent struct {
|
|||
UpdateTime int64 `json:"update_time"`
|
||||
}
|
||||
|
||||
type GetRedDot struct {
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// NotificationMsg notification message
|
||||
type NotificationMsg struct {
|
||||
// trigger notification user id
|
||||
|
@ -57,6 +64,8 @@ type ObjectInfo struct {
|
|||
type RedDot struct {
|
||||
Inbox int64 `json:"inbox"`
|
||||
Achievement int64 `json:"achievement"`
|
||||
Revision int64 `json:"revision"`
|
||||
CanRevision bool `json:"can_revision"`
|
||||
}
|
||||
|
||||
type NotificationSearch struct {
|
||||
|
@ -68,8 +77,11 @@ type NotificationSearch struct {
|
|||
}
|
||||
|
||||
type NotificationClearRequest struct {
|
||||
UserID string `json:"-"`
|
||||
TypeStr string `json:"type" form:"type"` // inbox achievement
|
||||
UserID string `json:"-"`
|
||||
TypeStr string `json:"type" form:"type"` // inbox achievement
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
}
|
||||
|
||||
type NotificationClearIDRequest struct {
|
||||
|
|
|
@ -3,8 +3,9 @@ package schema
|
|||
// RemoveQuestionReq delete question request
|
||||
type RemoveQuestionReq struct {
|
||||
// question id
|
||||
ID string `validate:"required" comment:"question id" json:"id"`
|
||||
UserID string `json:"-" ` // user_id
|
||||
ID string `validate:"required" comment:"question id" json:"id"`
|
||||
UserID string `json:"-" ` // user_id
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
type CloseQuestionReq struct {
|
||||
|
@ -12,6 +13,7 @@ type CloseQuestionReq struct {
|
|||
UserID string `json:"-" ` // user_id
|
||||
CloseType int `json:"close_type" ` // close_type
|
||||
CloseMsg string `json:"close_msg" ` // close_type
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
type CloseQuestionMeta struct {
|
||||
|
@ -30,6 +32,26 @@ type QuestionAdd struct {
|
|||
Tags []*TagItem `validate:"required,dive" json:"tags"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
QuestionPermission
|
||||
}
|
||||
|
||||
type QuestionPermission struct {
|
||||
// whether user can add it
|
||||
CanAdd bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
// whether user can close it
|
||||
CanClose bool `json:"-"`
|
||||
}
|
||||
|
||||
type CheckCanQuestionUpdate struct {
|
||||
// question id
|
||||
ID string `validate:"required" form:"id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
type QuestionUpdate struct {
|
||||
|
@ -46,7 +68,10 @@ type QuestionUpdate struct {
|
|||
// edit summary
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
NoNeedReview bool `json:"-"`
|
||||
QuestionPermission
|
||||
}
|
||||
|
||||
type QuestionBaseInfo struct {
|
||||
|
@ -81,6 +106,8 @@ type QuestionInfo struct {
|
|||
Status int `json:"status"`
|
||||
Operation *Operation `json:"operation,omitempty"`
|
||||
UserID string `json:"-" `
|
||||
LastEditUserID string `json:"-" `
|
||||
LastAnsweredUserID string `json:"-" `
|
||||
UserInfo *UserBasicInfo `json:"user_info"`
|
||||
UpdateUserInfo *UserBasicInfo `json:"update_user_info,omitempty"`
|
||||
LastAnsweredUserInfo *UserBasicInfo `json:"last_answered_user_info,omitempty"`
|
||||
|
@ -93,6 +120,11 @@ type QuestionInfo struct {
|
|||
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
||||
}
|
||||
|
||||
// UpdateQuestionResp update question resp
|
||||
type UpdateQuestionResp struct {
|
||||
WaitForReview bool `json:"wait_for_review"`
|
||||
}
|
||||
|
||||
type AdminQuestionInfo struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
|
@ -169,7 +201,7 @@ type CmsQuestionSearch struct {
|
|||
Page int `json:"page" form:"page"` // Query number of pages
|
||||
PageSize int `json:"page_size" form:"page_size"` // Search page size
|
||||
Status int `json:"-" form:"-"`
|
||||
StatusStr string `json:"status" form:"status"` // Status 1 Available 2 closed 10 UserDeleted
|
||||
StatusStr string `json:"status" form:"status"` // Status 1 Available 2 closed 10 UserDeleted
|
||||
Query string `validate:"omitempty,gt=0,lte=100" json:"query" form:"query" ` //Query string
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ package schema
|
|||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
)
|
||||
|
||||
// AddRevisionDTO add revision request
|
||||
|
@ -16,6 +18,8 @@ type AddRevisionDTO struct {
|
|||
Content string
|
||||
// log
|
||||
Log string
|
||||
// status
|
||||
Status int
|
||||
}
|
||||
|
||||
// GetRevisionListReq get revision list all request
|
||||
|
@ -24,6 +28,47 @@ type GetRevisionListReq struct {
|
|||
ObjectID string `validate:"required" comment:"object_id" form:"object_id"`
|
||||
}
|
||||
|
||||
const RevisionAuditApprove = "approve"
|
||||
const RevisionAuditReject = "reject"
|
||||
|
||||
type RevisionAuditReq struct {
|
||||
// object id
|
||||
ID string `validate:"required" comment:"id" form:"id"`
|
||||
Operation string `validate:"required" comment:"operation" form:"operation"` //approve or reject
|
||||
UserID string `json:"-"`
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
}
|
||||
|
||||
type RevisionSearch struct {
|
||||
Page int `json:"page" form:"page"` // Query number of pages
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (r RevisionSearch) GetCanReviewObjectTypes() []int {
|
||||
objectType := make([]int, 0)
|
||||
if r.CanReviewAnswer {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.AnswerObjectType])
|
||||
}
|
||||
if r.CanReviewQuestion {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.QuestionObjectType])
|
||||
}
|
||||
if r.CanReviewTag {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.TagObjectType])
|
||||
}
|
||||
return objectType
|
||||
}
|
||||
|
||||
type GetUnreviewedRevisionResp struct {
|
||||
Type string `json:"type"`
|
||||
Info *UnreviewedRevisionInfoInfo `json:"info"`
|
||||
UnreviewedInfo *GetRevisionResp `json:"unreviewed_info"`
|
||||
}
|
||||
|
||||
// GetRevisionResp get revision response
|
||||
type GetRevisionResp struct {
|
||||
// id
|
||||
|
|
|
@ -2,13 +2,21 @@ package schema
|
|||
|
||||
// SimpleObjectInfo simple object info
|
||||
type SimpleObjectInfo struct {
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectCreator string `json:"object_creator"`
|
||||
QuestionID string `json:"question_id"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
CommentID string `json:"comment_id"`
|
||||
TagID string `json:"tag_id"`
|
||||
ObjectType string `json:"object_type"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectCreatorUserID string `json:"object_creator_user_id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
CommentID string `json:"comment_id"`
|
||||
TagID string `json:"tag_id"`
|
||||
ObjectType string `json:"object_type"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type UnreviewedRevisionInfoInfo struct {
|
||||
ObjectID string `json:"object_id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Html string `json:"html"`
|
||||
Tags []*TagResp `json:"tags"`
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
// SiteGeneralReq site general request
|
||||
type SiteGeneralReq struct {
|
||||
Name string `validate:"required,gt=1,lte=128" form:"name" json:"name"`
|
||||
ShortDescription string `validate:"required,gt=3,lte=255" form:"short_description" json:"short_description"`
|
||||
Description string `validate:"required,gt=3,lte=2000" form:"description" json:"description"`
|
||||
ShortDescription string `validate:"omitempty,gt=3,lte=255" form:"short_description" json:"short_description"`
|
||||
Description string `validate:"omitempty,gt=3,lte=2000" form:"description" json:"description"`
|
||||
SiteUrl string `validate:"required,gt=1,lte=512,url" form:"site_url" json:"site_url"`
|
||||
ContactEmail string `validate:"required,gt=1,lte=512,email" form:"contact_email" json:"contact_email"`
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@ type GetTagInfoReq struct {
|
|||
Name string `validate:"omitempty,gt=0,lte=35" form:"name"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
}
|
||||
|
||||
func (r *GetTagInfoReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
|
@ -152,7 +156,8 @@ type UpdateTagReq struct {
|
|||
// edit summary
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
NoNeedReview bool `json:"-"`
|
||||
}
|
||||
|
||||
func (r *UpdateTagReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
|
@ -162,6 +167,11 @@ func (r *UpdateTagReq) Check() (errFields []*validator.FormErrorField, err error
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// UpdateTagResp update tag response
|
||||
type UpdateTagResp struct {
|
||||
WaitForReview bool `json:"wait_for_review"`
|
||||
}
|
||||
|
||||
// GetTagWithPageReq get tag list page request
|
||||
type GetTagWithPageReq struct {
|
||||
// page
|
||||
|
@ -182,10 +192,21 @@ type GetTagWithPageReq struct {
|
|||
type GetTagSynonymsReq struct {
|
||||
// tag_id
|
||||
TagID string `validate:"required" form:"tag_id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetTagSynonymsResp get tag synonyms response
|
||||
type GetTagSynonymsResp struct {
|
||||
// synonyms
|
||||
Synonyms []*TagSynonym `json:"synonyms"`
|
||||
// MemberActions
|
||||
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
||||
}
|
||||
|
||||
type TagSynonym struct {
|
||||
// tag id
|
||||
TagID string `json:"tag_id"`
|
||||
// slug name
|
||||
|
|
|
@ -368,7 +368,8 @@ type ActionRecordResp struct {
|
|||
}
|
||||
|
||||
type UserBasicInfo struct {
|
||||
ID string `json:"-" ` // user_id
|
||||
ID string `json:"-"` // user_id
|
||||
IsAdmin bool `json:"-"`
|
||||
Username string `json:"username" ` // name
|
||||
Rank int `json:"rank" ` // rank
|
||||
DisplayName string `json:"display_name"` // display_name
|
||||
|
|
|
@ -3,6 +3,7 @@ package schema
|
|||
type VoteReq struct {
|
||||
ObjectID string `validate:"required" form:"object_id" json:"object_id"` // id
|
||||
IsCancel bool `validate:"omitempty" form:"is_cancel" json:"is_cancel"` // is cancel
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type VoteDTO struct {
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
package activity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/repo/config"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/comment_common"
|
||||
"github.com/answerdev/answer/internal/service/object_info"
|
||||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
"github.com/answerdev/answer/internal/service/tag_common"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
"github.com/answerdev/answer/pkg/converter"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
// ActivityRepo activity repository
|
||||
type ActivityRepo interface {
|
||||
GetObjectAllActivity(ctx context.Context, objectID string, showVote bool) (activityList []*entity.Activity, err error)
|
||||
}
|
||||
|
||||
// ActivityService activity service
|
||||
type ActivityService struct {
|
||||
activityRepo ActivityRepo
|
||||
userCommon *usercommon.UserCommon
|
||||
activityCommonService *activity_common.ActivityCommon
|
||||
tagCommonService *tag_common.TagCommonService
|
||||
objectInfoService *object_info.ObjService
|
||||
commentCommonService *comment_common.CommentCommonService
|
||||
revisionService *revision_common.RevisionService
|
||||
}
|
||||
|
||||
// NewActivityService new activity service
|
||||
func NewActivityService(
|
||||
activityRepo ActivityRepo,
|
||||
userCommon *usercommon.UserCommon,
|
||||
activityCommonService *activity_common.ActivityCommon,
|
||||
tagCommonService *tag_common.TagCommonService,
|
||||
objectInfoService *object_info.ObjService,
|
||||
commentCommonService *comment_common.CommentCommonService,
|
||||
revisionService *revision_common.RevisionService,
|
||||
) *ActivityService {
|
||||
return &ActivityService{
|
||||
objectInfoService: objectInfoService,
|
||||
activityRepo: activityRepo,
|
||||
userCommon: userCommon,
|
||||
activityCommonService: activityCommonService,
|
||||
tagCommonService: tagCommonService,
|
||||
commentCommonService: commentCommonService,
|
||||
revisionService: revisionService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetObjectTimeline get object timeline
|
||||
func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.GetObjectTimelineReq) (
|
||||
resp *schema.GetObjectTimelineResp, err error) {
|
||||
resp = &schema.GetObjectTimelineResp{
|
||||
ObjectInfo: &schema.ActObjectInfo{},
|
||||
Timeline: make([]*schema.ActObjectTimeline, 0),
|
||||
}
|
||||
|
||||
objInfo, err := as.objectInfoService.GetInfo(ctx, req.ObjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.ObjectInfo.Title = objInfo.Title
|
||||
resp.ObjectInfo.ObjectType = objInfo.ObjectType
|
||||
resp.ObjectInfo.QuestionID = objInfo.QuestionID
|
||||
resp.ObjectInfo.AnswerID = objInfo.AnswerID
|
||||
if len(objInfo.ObjectCreatorUserID) > 0 {
|
||||
// get object creator user info
|
||||
userBasicInfo, exist, err := as.userCommon.GetUserBasicInfoByID(ctx, objInfo.ObjectCreatorUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
resp.ObjectInfo.Username = userBasicInfo.Username
|
||||
resp.ObjectInfo.DisplayName = userBasicInfo.DisplayName
|
||||
}
|
||||
}
|
||||
|
||||
activityList, err := as.activityRepo.GetObjectAllActivity(ctx, req.ObjectID, req.ShowVote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, act := range activityList {
|
||||
item := &schema.ActObjectTimeline{
|
||||
ActivityID: act.ID,
|
||||
RevisionID: converter.IntToString(act.RevisionID),
|
||||
CreatedAt: act.CreatedAt.Unix(),
|
||||
Cancelled: act.Cancelled == entity.ActivityCancelled,
|
||||
ObjectID: act.ObjectID,
|
||||
}
|
||||
if item.Cancelled {
|
||||
item.CancelledAt = act.CancelledAt.Unix()
|
||||
}
|
||||
|
||||
// database save activity type is number, change to activity type string is like "question.asked".
|
||||
// so we need to cut the front part of '.'
|
||||
item.ObjectType, item.ActivityType, _ = strings.Cut(config.ID2KeyMapping[act.ActivityType], ".")
|
||||
|
||||
isHidden, formattedActivityType := formatActivity(item.ActivityType)
|
||||
if isHidden {
|
||||
continue
|
||||
}
|
||||
item.ActivityType = formattedActivityType
|
||||
|
||||
// get user info
|
||||
userBasicInfo, exist, err := as.userCommon.GetUserBasicInfoByID(ctx, act.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
item.Username = userBasicInfo.Username
|
||||
item.UserDisplayName = userBasicInfo.DisplayName
|
||||
}
|
||||
|
||||
if item.ObjectType == constant.CommentObjectType {
|
||||
comment, err := as.commentCommonService.GetComment(ctx, item.ObjectID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
item.Comment = comment.ParsedText
|
||||
}
|
||||
}
|
||||
|
||||
resp.Timeline = append(resp.Timeline, item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetObjectTimelineDetail get object timeline
|
||||
func (as *ActivityService) GetObjectTimelineDetail(ctx context.Context, req *schema.GetObjectTimelineDetailReq) (
|
||||
resp *schema.GetObjectTimelineDetailResp, err error) {
|
||||
resp = &schema.GetObjectTimelineDetailResp{}
|
||||
resp.OldRevision, err = as.getOneObjectDetail(ctx, req.OldRevisionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.NewRevision, err = as.getOneObjectDetail(ctx, req.NewRevisionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetObjectTimelineDetail get object detail
|
||||
func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID string) (
|
||||
resp *schema.ObjectTimelineDetail, err error) {
|
||||
resp = &schema.ObjectTimelineDetail{Tags: make([]string, 0)}
|
||||
|
||||
revision, err := as.revisionService.GetRevision(ctx, revisionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objInfo, err := as.objectInfoService.GetInfo(ctx, revision.ObjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch objInfo.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
data := &entity.QuestionWithTagsRevision{}
|
||||
if err = json.Unmarshal([]byte(revision.Content), data); err != nil {
|
||||
log.Errorf("revision parsing error %s", err)
|
||||
return resp, nil
|
||||
}
|
||||
for _, tag := range data.Tags {
|
||||
resp.Tags = append(resp.Tags, tag.SlugName)
|
||||
}
|
||||
resp.Title = data.Title
|
||||
resp.OriginalText = data.OriginalText
|
||||
case constant.AnswerObjectType:
|
||||
data := &entity.Answer{}
|
||||
if err = json.Unmarshal([]byte(revision.Content), data); err != nil {
|
||||
log.Errorf("revision parsing error %s", err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.Title = objInfo.Title // answer show question title
|
||||
resp.OriginalText = data.OriginalText
|
||||
case constant.TagObjectType:
|
||||
data := &entity.Tag{}
|
||||
if err = json.Unmarshal([]byte(revision.Content), data); err != nil {
|
||||
log.Errorf("revision parsing error %s", err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.Title = data.SlugName
|
||||
resp.OriginalText = data.OriginalText
|
||||
default:
|
||||
log.Errorf("unknown object type %s", objInfo.ObjectType)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func formatActivity(activityType string) (isHidden bool, formattedActivityType string) {
|
||||
if activityType == "voted_up" || activityType == "voted_down" || activityType == "accepted" {
|
||||
return true, ""
|
||||
}
|
||||
if activityType == "vote_up" {
|
||||
return false, "upvote"
|
||||
}
|
||||
if activityType == "vote_down" {
|
||||
return false, "downvote"
|
||||
}
|
||||
return false, activityType
|
||||
}
|
|
@ -10,9 +10,9 @@ import (
|
|||
// AnswerActivityRepo answer activity
|
||||
type AnswerActivityRepo interface {
|
||||
AcceptAnswer(ctx context.Context,
|
||||
answerObjID, questionUserID, answerUserID string, isSelf bool) (err error)
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool) (err error)
|
||||
CancelAcceptAnswer(ctx context.Context,
|
||||
answerObjID, questionUserID, answerUserID string) (err error)
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string) (err error)
|
||||
DeleteAnswer(ctx context.Context, answerID string) (err error)
|
||||
}
|
||||
|
||||
|
@ -38,14 +38,14 @@ func NewAnswerActivityService(
|
|||
|
||||
// AcceptAnswer accept answer change activity
|
||||
func (as *AnswerActivityService) AcceptAnswer(ctx context.Context,
|
||||
answerObjID, questionUserID, answerUserID string, isSelf bool) (err error) {
|
||||
return as.answerActivityRepo.AcceptAnswer(ctx, answerObjID, questionUserID, answerUserID, isSelf)
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool) (err error) {
|
||||
return as.answerActivityRepo.AcceptAnswer(ctx, answerObjID, questionObjID, questionUserID, answerUserID, isSelf)
|
||||
}
|
||||
|
||||
// CancelAcceptAnswer cancel accept answer change activity
|
||||
func (as *AnswerActivityService) CancelAcceptAnswer(ctx context.Context,
|
||||
answerObjID, questionUserID, answerUserID string) (err error) {
|
||||
return as.answerActivityRepo.CancelAcceptAnswer(ctx, answerObjID, questionUserID, answerUserID)
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string) (err error) {
|
||||
return as.answerActivityRepo.CancelAcceptAnswer(ctx, answerObjID, questionObjID, questionUserID, answerUserID)
|
||||
}
|
||||
|
||||
// DeleteAnswer delete answer change activity
|
||||
|
|
|
@ -4,6 +4,9 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
"github.com/answerdev/answer/pkg/converter"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
|
@ -13,4 +16,56 @@ type ActivityRepo interface {
|
|||
GetActivity(ctx context.Context, session *xorm.Session, objectID, userID string, activityType int) (
|
||||
existsActivity *entity.Activity, exist bool, err error)
|
||||
GetUserIDObjectIDActivitySum(ctx context.Context, userID, objectID string) (int, error)
|
||||
GetActivityTypeByConfigKey(ctx context.Context, configKey string) (activityType int, err error)
|
||||
AddActivity(ctx context.Context, activity *entity.Activity) (err error)
|
||||
}
|
||||
|
||||
type ActivityCommon struct {
|
||||
activityRepo ActivityRepo
|
||||
}
|
||||
|
||||
// NewActivityCommon new activity common
|
||||
func NewActivityCommon(
|
||||
activityRepo ActivityRepo,
|
||||
) *ActivityCommon {
|
||||
activity := &ActivityCommon{
|
||||
activityRepo: activityRepo,
|
||||
}
|
||||
activity.HandleActivity()
|
||||
return activity
|
||||
}
|
||||
|
||||
// HandleActivity handle activity message
|
||||
func (ac *ActivityCommon) HandleActivity() {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
for msg := range activity_queue.ActivityQueue {
|
||||
log.Debugf("received activity %+v", msg)
|
||||
|
||||
activityType, err := ac.activityRepo.GetActivityTypeByConfigKey(context.Background(), string(msg.ActivityTypeKey))
|
||||
if err != nil {
|
||||
log.Errorf("error getting activity type %s, activity type is %d", err, activityType)
|
||||
}
|
||||
|
||||
act := &entity.Activity{
|
||||
UserID: msg.UserID,
|
||||
TriggerUserID: msg.TriggerUserID,
|
||||
ObjectID: msg.ObjectID,
|
||||
OriginalObjectID: msg.OriginalObjectID,
|
||||
ActivityType: activityType,
|
||||
Cancelled: entity.ActivityAvailable,
|
||||
}
|
||||
if len(msg.RevisionID) > 0 {
|
||||
act.RevisionID = converter.StringToInt64(msg.RevisionID)
|
||||
}
|
||||
if err := ac.activityRepo.AddActivity(context.TODO(), act); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package activity_queue
|
||||
|
||||
import (
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
ActivityQueue = make(chan *schema.ActivityMsg, 128)
|
||||
)
|
||||
|
||||
// AddActivity add new activity
|
||||
func AddActivity(msg *schema.ActivityMsg) {
|
||||
ActivityQueue <- msg
|
||||
}
|
|
@ -68,7 +68,11 @@ func (as *AnswerCommon) ShowFormat(ctx context.Context, data *entity.Answer) *sc
|
|||
info.VoteCount = data.VoteCount
|
||||
info.CreateTime = data.CreatedAt.Unix()
|
||||
info.UpdateTime = data.UpdatedAt.Unix()
|
||||
if data.UpdatedAt.Unix() < 1 {
|
||||
info.UpdateTime = 0
|
||||
}
|
||||
info.UserID = data.UserID
|
||||
info.UpdateUserID = data.LastEditUserID
|
||||
return &info
|
||||
}
|
||||
|
||||
|
@ -80,7 +84,11 @@ func (as *AnswerCommon) AdminShowFormat(ctx context.Context, data *entity.Answer
|
|||
info.VoteCount = data.VoteCount
|
||||
info.CreateTime = data.CreatedAt.Unix()
|
||||
info.UpdateTime = data.UpdatedAt.Unix()
|
||||
if data.UpdatedAt.Unix() < 1 {
|
||||
info.UpdateTime = 0
|
||||
}
|
||||
info.UserID = data.UserID
|
||||
info.UpdateUserID = data.LastEditUserID
|
||||
info.Description = htmltext.FetchExcerpt(data.ParsedText, "...", 240)
|
||||
return &info
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/service/activity"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
"github.com/answerdev/answer/internal/service/notice_queue"
|
||||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
|
||||
|
@ -73,27 +74,29 @@ func (as *AnswerService) RemoveAnswer(ctx context.Context, req *schema.RemoveAns
|
|||
if !exist {
|
||||
return nil
|
||||
}
|
||||
if answerInfo.UserID != req.UserID {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
}
|
||||
if answerInfo.VoteCount > 0 {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
}
|
||||
if answerInfo.Adopted == schema.AnswerAdoptedEnable {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
}
|
||||
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, answerInfo.QuestionID)
|
||||
if err != nil {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
}
|
||||
if !exist {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
}
|
||||
if questionInfo.AnswerCount > 1 {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
}
|
||||
if questionInfo.AcceptedAnswerID != "" {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
if !req.IsAdmin {
|
||||
if answerInfo.UserID != req.UserID {
|
||||
return errors.BadRequest(reason.AnswerCannotDeleted)
|
||||
}
|
||||
if answerInfo.VoteCount > 0 {
|
||||
return errors.BadRequest(reason.AnswerCannotDeleted)
|
||||
}
|
||||
if answerInfo.Adopted == schema.AnswerAdoptedEnable {
|
||||
return errors.BadRequest(reason.AnswerCannotDeleted)
|
||||
}
|
||||
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, answerInfo.QuestionID)
|
||||
if err != nil {
|
||||
return errors.BadRequest(reason.AnswerCannotDeleted)
|
||||
}
|
||||
if !exist {
|
||||
return errors.BadRequest(reason.AnswerCannotDeleted)
|
||||
}
|
||||
if questionInfo.AnswerCount > 1 {
|
||||
return errors.BadRequest(reason.AnswerCannotDeleted)
|
||||
}
|
||||
if questionInfo.AcceptedAnswerID != "" {
|
||||
return errors.BadRequest(reason.AnswerCannotDeleted)
|
||||
}
|
||||
}
|
||||
|
||||
// user add question count
|
||||
|
@ -126,7 +129,6 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (
|
|||
if !exist {
|
||||
return "", errors.BadRequest(reason.QuestionNotFound)
|
||||
}
|
||||
now := time.Now()
|
||||
insertData := new(entity.Answer)
|
||||
insertData.UserID = req.UserID
|
||||
insertData.OriginalText = req.Content
|
||||
|
@ -135,7 +137,7 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (
|
|||
insertData.QuestionID = req.QuestionID
|
||||
insertData.RevisionID = "0"
|
||||
insertData.Status = entity.AnswerStatusAvailable
|
||||
insertData.UpdatedAt = now
|
||||
//insertData.UpdatedAt = now
|
||||
if err = as.answerRepo.AddAnswer(ctx, insertData); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -164,15 +166,40 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (
|
|||
}
|
||||
infoJSON, _ := json.Marshal(insertData)
|
||||
revisionDTO.Content = string(infoJSON)
|
||||
err = as.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
revisionID, err := as.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
if err != nil {
|
||||
return insertData.ID, err
|
||||
}
|
||||
as.notificationAnswerTheQuestion(ctx, questionInfo.UserID, insertData.ID, req.UserID)
|
||||
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: insertData.UserID,
|
||||
ObjectID: insertData.ID,
|
||||
OriginalObjectID: insertData.ID,
|
||||
ActivityTypeKey: constant.ActAnswerAnswered,
|
||||
RevisionID: revisionID,
|
||||
})
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: insertData.UserID,
|
||||
ObjectID: insertData.ID,
|
||||
OriginalObjectID: questionInfo.ID,
|
||||
ActivityTypeKey: constant.ActQuestionAnswered,
|
||||
})
|
||||
return insertData.ID, nil
|
||||
}
|
||||
|
||||
func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq) (string, error) {
|
||||
//req.NoNeedReview //true 不需要审核
|
||||
var canUpdate bool
|
||||
_, existUnreviewed, err := as.revisionService.ExistUnreviewedByObjectID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if existUnreviewed {
|
||||
err = errors.BadRequest(reason.AnswerCannotUpdate)
|
||||
return "", err
|
||||
}
|
||||
|
||||
questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -180,34 +207,75 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq
|
|||
if !exist {
|
||||
return "", errors.BadRequest(reason.QuestionNotFound)
|
||||
}
|
||||
|
||||
answerInfo, exist, err := as.answerRepo.GetByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !exist {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
//If the content is the same, ignore it
|
||||
if answerInfo.OriginalText == req.Content {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
insertData := new(entity.Answer)
|
||||
insertData.ID = req.ID
|
||||
insertData.UserID = answerInfo.UserID
|
||||
insertData.QuestionID = req.QuestionID
|
||||
insertData.UserID = req.UserID
|
||||
insertData.OriginalText = req.Content
|
||||
insertData.ParsedText = req.HTML
|
||||
insertData.UpdatedAt = now
|
||||
if err = as.answerRepo.UpdateAnswer(ctx, insertData, []string{"original_text", "parsed_text", "update_time"}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = as.questionCommon.UpdataPostTime(ctx, req.QuestionID)
|
||||
if err != nil {
|
||||
return insertData.ID, err
|
||||
|
||||
insertData.LastEditUserID = "0"
|
||||
if answerInfo.UserID != req.UserID {
|
||||
insertData.LastEditUserID = req.UserID
|
||||
}
|
||||
|
||||
revisionDTO := &schema.AddRevisionDTO{
|
||||
UserID: req.UserID,
|
||||
ObjectID: req.ID,
|
||||
Title: "",
|
||||
Log: req.EditSummary,
|
||||
}
|
||||
|
||||
if req.NoNeedReview || answerInfo.UserID == req.UserID {
|
||||
canUpdate = true
|
||||
}
|
||||
|
||||
if !canUpdate {
|
||||
revisionDTO.Status = entity.RevisionUnreviewedStatus
|
||||
} else {
|
||||
if err = as.answerRepo.UpdateAnswer(ctx, insertData, []string{"original_text", "parsed_text", "update_time"}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = as.questionCommon.UpdataPostTime(ctx, req.QuestionID)
|
||||
if err != nil {
|
||||
return insertData.ID, err
|
||||
}
|
||||
as.notificationUpdateAnswer(ctx, questionInfo.UserID, insertData.ID, req.UserID)
|
||||
revisionDTO.Status = entity.RevisionReviewPassStatus
|
||||
}
|
||||
|
||||
infoJSON, _ := json.Marshal(insertData)
|
||||
revisionDTO.Content = string(infoJSON)
|
||||
err = as.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
revisionID, err := as.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
if err != nil {
|
||||
return insertData.ID, err
|
||||
}
|
||||
as.notificationUpdateAnswer(ctx, questionInfo.UserID, insertData.ID, req.UserID)
|
||||
if canUpdate {
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: insertData.UserID,
|
||||
ObjectID: insertData.ID,
|
||||
OriginalObjectID: insertData.ID,
|
||||
ActivityTypeKey: constant.ActAnswerEdited,
|
||||
RevisionID: revisionID,
|
||||
})
|
||||
}
|
||||
|
||||
return insertData.ID, nil
|
||||
}
|
||||
|
||||
|
@ -276,14 +344,14 @@ func (as *AnswerService) updateAnswerRank(ctx context.Context, userID string,
|
|||
// if this question is already been answered, should cancel old answer rank
|
||||
if oldAnswerInfo != nil {
|
||||
err := as.answerActivityService.CancelAcceptAnswer(
|
||||
ctx, questionInfo.AcceptedAnswerID, questionInfo.UserID, oldAnswerInfo.UserID)
|
||||
ctx, questionInfo.AcceptedAnswerID, questionInfo.ID, questionInfo.UserID, oldAnswerInfo.UserID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
if newAnswerInfo.ID != "" {
|
||||
err := as.answerActivityService.AcceptAnswer(
|
||||
ctx, newAnswerInfo.ID, questionInfo.UserID, newAnswerInfo.UserID, newAnswerInfo.UserID == userID)
|
||||
ctx, newAnswerInfo.ID, questionInfo.ID, questionInfo.UserID, newAnswerInfo.UserID, newAnswerInfo.UserID == userID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
@ -348,7 +416,7 @@ func (as *AnswerService) AdminSetAnswerStatus(ctx context.Context, answerID stri
|
|||
}
|
||||
|
||||
if setStatus == entity.AnswerStatusDeleted {
|
||||
err = as.answerActivityService.DeleteQuestion(ctx, answerInfo.ID, answerInfo.CreatedAt, answerInfo.VoteCount)
|
||||
err = as.answerActivityService.DeleteAnswer(ctx, answerInfo.ID, answerInfo.CreatedAt, answerInfo.VoteCount)
|
||||
if err != nil {
|
||||
log.Errorf("admin delete question then rank rollback error %s", err.Error())
|
||||
}
|
||||
|
@ -366,39 +434,40 @@ func (as *AnswerService) AdminSetAnswerStatus(ctx context.Context, answerID stri
|
|||
return nil
|
||||
}
|
||||
|
||||
func (as *AnswerService) SearchList(ctx context.Context, search *schema.AnswerList) ([]*schema.AnswerInfo, int64, error) {
|
||||
func (as *AnswerService) SearchList(ctx context.Context, req *schema.AnswerListReq) ([]*schema.AnswerInfo, int64, error) {
|
||||
list := make([]*schema.AnswerInfo, 0)
|
||||
dbSearch := entity.AnswerSearch{}
|
||||
dbSearch.QuestionID = search.QuestionID
|
||||
dbSearch.Page = search.Page
|
||||
dbSearch.PageSize = search.PageSize
|
||||
dbSearch.Order = search.Order
|
||||
dblist, count, err := as.answerRepo.SearchList(ctx, &dbSearch)
|
||||
dbSearch.QuestionID = req.QuestionID
|
||||
dbSearch.Page = req.Page
|
||||
dbSearch.PageSize = req.PageSize
|
||||
dbSearch.Order = req.Order
|
||||
answerOriginalList, count, err := as.answerRepo.SearchList(ctx, &dbSearch)
|
||||
if err != nil {
|
||||
return list, count, err
|
||||
}
|
||||
AnswerList, err := as.SearchFormatInfo(ctx, dblist, search.LoginUserID)
|
||||
answerList, err := as.SearchFormatInfo(ctx, answerOriginalList, req)
|
||||
if err != nil {
|
||||
return AnswerList, count, err
|
||||
return answerList, count, err
|
||||
}
|
||||
return AnswerList, count, nil
|
||||
return answerList, count, nil
|
||||
}
|
||||
|
||||
func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.Answer, loginUserID string) ([]*schema.AnswerInfo, error) {
|
||||
func (as *AnswerService) SearchFormatInfo(ctx context.Context, answers []*entity.Answer, req *schema.AnswerListReq) (
|
||||
[]*schema.AnswerInfo, error) {
|
||||
list := make([]*schema.AnswerInfo, 0)
|
||||
objectIds := make([]string, 0)
|
||||
userIds := make([]string, 0)
|
||||
for _, dbitem := range dblist {
|
||||
item := as.ShowFormat(ctx, dbitem)
|
||||
objectIDs := make([]string, 0)
|
||||
userIDs := make([]string, 0)
|
||||
for _, info := range answers {
|
||||
item := as.ShowFormat(ctx, info)
|
||||
list = append(list, item)
|
||||
objectIds = append(objectIds, dbitem.ID)
|
||||
userIds = append(userIds, dbitem.UserID)
|
||||
if loginUserID != "" {
|
||||
// item.VoteStatus = as.activityFunc.GetVoteStatus(ctx, item.TagID, loginUserId)
|
||||
item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, loginUserID)
|
||||
objectIDs = append(objectIDs, info.ID)
|
||||
userIDs = append(userIDs, info.UserID)
|
||||
userIDs = append(userIDs, info.LastEditUserID)
|
||||
if req.UserID != "" {
|
||||
item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, req.UserID)
|
||||
}
|
||||
}
|
||||
userInfoMap, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIds)
|
||||
userInfoMap, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIDs)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
@ -406,30 +475,32 @@ func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.
|
|||
_, ok := userInfoMap[item.UserID]
|
||||
if ok {
|
||||
item.UserInfo = userInfoMap[item.UserID]
|
||||
item.UpdateUserInfo = userInfoMap[item.UserID]
|
||||
}
|
||||
_, ok = userInfoMap[item.UpdateUserID]
|
||||
if ok {
|
||||
item.UpdateUserInfo = userInfoMap[item.UpdateUserID]
|
||||
}
|
||||
}
|
||||
|
||||
if loginUserID == "" {
|
||||
if req.UserID == "" {
|
||||
return list, nil
|
||||
}
|
||||
|
||||
CollectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserID, objectIds)
|
||||
searchObjectCollected, err := as.collectionCommon.SearchObjectCollected(ctx, req.UserID, objectIDs)
|
||||
if err != nil {
|
||||
log.Error("CollectionFunc.SearchObjectCollected error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
_, ok := CollectedMap[item.ID]
|
||||
_, ok := searchObjectCollected[item.ID]
|
||||
if ok {
|
||||
item.Collected = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
item.MemberActions = permission.GetAnswerPermission(loginUserID, item.UserID)
|
||||
item.MemberActions = permission.GetAnswerPermission(ctx, req.UserID, item.UserID, req.CanEdit, req.CanDelete)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ func (as *AuthService) GetUserCacheInfo(ctx context.Context, accessToken string)
|
|||
}
|
||||
cacheInfo, _ := as.authRepo.GetUserStatus(ctx, userCacheInfo.UserID)
|
||||
if cacheInfo != nil {
|
||||
log.Infof("user status updated: %+v", cacheInfo)
|
||||
log.Debugf("user status updated: %+v", cacheInfo)
|
||||
userCacheInfo.UserStatus = cacheInfo.UserStatus
|
||||
userCacheInfo.EmailStatus = cacheInfo.EmailStatus
|
||||
userCacheInfo.IsAdmin = cacheInfo.IsAdmin
|
||||
|
|
|
@ -9,9 +9,10 @@ import (
|
|||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
"github.com/answerdev/answer/internal/service/comment_common"
|
||||
"github.com/answerdev/answer/internal/service/notice_queue"
|
||||
object_info "github.com/answerdev/answer/internal/service/object_info"
|
||||
"github.com/answerdev/answer/internal/service/object_info"
|
||||
"github.com/answerdev/answer/internal/service/permission"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
"github.com/jinzhu/copier"
|
||||
|
@ -111,9 +112,9 @@ func (cs *CommentService) AddComment(ctx context.Context, req *schema.AddComment
|
|||
}
|
||||
|
||||
if objInfo.ObjectType == constant.QuestionObjectType {
|
||||
cs.notificationQuestionComment(ctx, objInfo.ObjectCreator, comment.ID, req.UserID)
|
||||
cs.notificationQuestionComment(ctx, objInfo.ObjectCreatorUserID, comment.ID, req.UserID)
|
||||
} else if objInfo.ObjectType == constant.AnswerObjectType {
|
||||
cs.notificationAnswerComment(ctx, objInfo.ObjectCreator, comment.ID, req.UserID)
|
||||
cs.notificationAnswerComment(ctx, objInfo.ObjectCreatorUserID, comment.ID, req.UserID)
|
||||
}
|
||||
if len(req.MentionUsernameList) > 0 {
|
||||
cs.notificationMention(ctx, req.MentionUsernameList, comment.ID, req.UserID)
|
||||
|
@ -121,7 +122,7 @@ func (cs *CommentService) AddComment(ctx context.Context, req *schema.AddComment
|
|||
|
||||
resp = &schema.GetCommentResp{}
|
||||
resp.SetFromComment(comment)
|
||||
resp.MemberActions = permission.GetCommentPermission(req.UserID, resp.UserID)
|
||||
resp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, resp.UserID, req.CanEdit, req.CanDelete)
|
||||
|
||||
// get reply user info
|
||||
if len(resp.ReplyUserID) > 0 {
|
||||
|
@ -148,6 +149,13 @@ func (cs *CommentService) AddComment(ctx context.Context, req *schema.AddComment
|
|||
resp.UserAvatar = userInfo.Avatar
|
||||
resp.UserStatus = userInfo.Status
|
||||
}
|
||||
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: comment.UserID,
|
||||
ObjectID: comment.ID,
|
||||
OriginalObjectID: req.ObjectID,
|
||||
ActivityTypeKey: constant.ActQuestionCommented,
|
||||
})
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -222,7 +230,7 @@ func (cs *CommentService) GetComment(ctx context.Context, req *schema.GetComment
|
|||
// check if current user vote this comment
|
||||
resp.IsVote = cs.checkIsVote(ctx, req.UserID, resp.CommentID)
|
||||
|
||||
resp.MemberActions = permission.GetCommentPermission(req.UserID, resp.UserID)
|
||||
resp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, resp.UserID, req.CanEdit, req.CanDelete)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -282,7 +290,7 @@ func (cs *CommentService) GetCommentWithPage(ctx context.Context, req *schema.Ge
|
|||
// check if current user vote this comment
|
||||
commentResp.IsVote = cs.checkIsVote(ctx, req.UserID, commentResp.CommentID)
|
||||
|
||||
commentResp.MemberActions = permission.GetCommentPermission(req.UserID, commentResp.UserID)
|
||||
commentResp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, commentResp.UserID, req.CanEdit, req.CanDelete)
|
||||
resp = append(resp, commentResp)
|
||||
}
|
||||
return pager.NewPageModel(total, resp), nil
|
||||
|
|
|
@ -11,6 +11,8 @@ import (
|
|||
"github.com/answerdev/answer/internal/base/translator"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
notficationcommon "github.com/answerdev/answer/internal/service/notification_common"
|
||||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
@ -20,24 +22,28 @@ type NotificationService struct {
|
|||
data *data.Data
|
||||
notificationRepo notficationcommon.NotificationRepo
|
||||
notificationCommon *notficationcommon.NotificationCommon
|
||||
revisionService *revision_common.RevisionService
|
||||
}
|
||||
|
||||
func NewNotificationService(
|
||||
data *data.Data,
|
||||
notificationRepo notficationcommon.NotificationRepo,
|
||||
notificationCommon *notficationcommon.NotificationCommon,
|
||||
revisionService *revision_common.RevisionService,
|
||||
|
||||
) *NotificationService {
|
||||
return &NotificationService{
|
||||
data: data,
|
||||
notificationRepo: notificationRepo,
|
||||
notificationCommon: notificationCommon,
|
||||
revisionService: revisionService,
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NotificationService) GetRedDot(ctx context.Context, userID string) (*schema.RedDot, error) {
|
||||
func (ns *NotificationService) GetRedDot(ctx context.Context, req *schema.GetRedDot) (*schema.RedDot, error) {
|
||||
redBot := &schema.RedDot{}
|
||||
inboxKey := fmt.Sprintf("answer_RedDot_%d_%s", schema.NotificationTypeInbox, userID)
|
||||
achievementKey := fmt.Sprintf("answer_RedDot_%d_%s", schema.NotificationTypeAchievement, userID)
|
||||
inboxKey := fmt.Sprintf("answer_RedDot_%d_%s", schema.NotificationTypeInbox, req.UserID)
|
||||
achievementKey := fmt.Sprintf("answer_RedDot_%d_%s", schema.NotificationTypeAchievement, req.UserID)
|
||||
inboxValue, err := ns.data.Cache.GetInt64(ctx, inboxKey)
|
||||
if err != nil {
|
||||
redBot.Inbox = 0
|
||||
|
@ -50,19 +56,32 @@ func (ns *NotificationService) GetRedDot(ctx context.Context, userID string) (*s
|
|||
} else {
|
||||
redBot.Achievement = achievementValue
|
||||
}
|
||||
revisionCount := &schema.RevisionSearch{}
|
||||
_ = copier.Copy(revisionCount, req)
|
||||
if req.CanReviewAnswer || req.CanReviewQuestion || req.CanReviewTag {
|
||||
redBot.CanRevision = true
|
||||
revisionCountNum, err := ns.revisionService.GetUnreviewedRevisionCount(ctx, revisionCount)
|
||||
if err != nil {
|
||||
return redBot, err
|
||||
}
|
||||
redBot.Revision = revisionCountNum
|
||||
}
|
||||
|
||||
return redBot, nil
|
||||
}
|
||||
|
||||
func (ns *NotificationService) ClearRedDot(ctx context.Context, userID string, botTypeStr string) (*schema.RedDot, error) {
|
||||
botType, ok := schema.NotificationType[botTypeStr]
|
||||
func (ns *NotificationService) ClearRedDot(ctx context.Context, req *schema.NotificationClearRequest) (*schema.RedDot, error) {
|
||||
botType, ok := schema.NotificationType[req.TypeStr]
|
||||
if ok {
|
||||
key := fmt.Sprintf("answer_RedDot_%d_%s", botType, userID)
|
||||
key := fmt.Sprintf("answer_RedDot_%d_%s", botType, req.UserID)
|
||||
err := ns.data.Cache.Del(ctx, key)
|
||||
if err != nil {
|
||||
log.Error("ClearRedDot del cache error", err.Error())
|
||||
}
|
||||
}
|
||||
return ns.GetRedDot(ctx, userID)
|
||||
getRedDotreq := &schema.GetRedDot{}
|
||||
_ = copier.Copy(getRedDotreq, req)
|
||||
return ns.GetRedDot(ctx, getRedDotreq)
|
||||
}
|
||||
|
||||
func (ns *NotificationService) ClearUnRead(ctx context.Context, userID string, botTypeStr string) error {
|
||||
|
|
|
@ -20,6 +20,7 @@ type ObjService struct {
|
|||
questionRepo questioncommon.QuestionRepo
|
||||
commentRepo comment_common.CommentCommonRepo
|
||||
tagRepo tagcommon.TagCommonRepo
|
||||
tagCommon *tagcommon.TagCommonService
|
||||
}
|
||||
|
||||
// NewObjService new object service
|
||||
|
@ -27,14 +28,92 @@ func NewObjService(
|
|||
answerRepo answercommon.AnswerRepo,
|
||||
questionRepo questioncommon.QuestionRepo,
|
||||
commentRepo comment_common.CommentCommonRepo,
|
||||
tagRepo tagcommon.TagCommonRepo) *ObjService {
|
||||
tagRepo tagcommon.TagCommonRepo,
|
||||
tagCommon *tagcommon.TagCommonService,
|
||||
) *ObjService {
|
||||
return &ObjService{
|
||||
answerRepo: answerRepo,
|
||||
questionRepo: questionRepo,
|
||||
commentRepo: commentRepo,
|
||||
tagRepo: tagRepo,
|
||||
tagCommon: tagCommon,
|
||||
}
|
||||
}
|
||||
func (os *ObjService) GetUnreviewedRevisionInfo(ctx context.Context, objectID string) (objInfo *schema.UnreviewedRevisionInfoInfo, err error) {
|
||||
objInfo = &schema.UnreviewedRevisionInfoInfo{}
|
||||
|
||||
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch objectType {
|
||||
case constant.QuestionObjectType:
|
||||
questionInfo, exist, err := os.questionRepo.GetQuestion(ctx, objectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
break
|
||||
}
|
||||
taglist, err := os.tagCommon.GetObjectEntityTag(ctx, objectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.tagCommon.TagsFormatRecommendAndReserved(ctx, taglist)
|
||||
tags, err := os.tagCommon.TagFormat(ctx, taglist)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objInfo = &schema.UnreviewedRevisionInfoInfo{
|
||||
ObjectID: questionInfo.ID,
|
||||
Title: questionInfo.Title,
|
||||
Content: questionInfo.OriginalText,
|
||||
Html: questionInfo.ParsedText,
|
||||
Tags: tags,
|
||||
}
|
||||
case constant.AnswerObjectType:
|
||||
answerInfo, exist, err := os.answerRepo.GetAnswer(ctx, objectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
break
|
||||
}
|
||||
|
||||
questionInfo, exist, err := os.questionRepo.GetQuestion(ctx, answerInfo.QuestionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
break
|
||||
}
|
||||
objInfo = &schema.UnreviewedRevisionInfoInfo{
|
||||
ObjectID: answerInfo.ID,
|
||||
Title: questionInfo.Title,
|
||||
Content: answerInfo.OriginalText,
|
||||
Html: answerInfo.ParsedText,
|
||||
}
|
||||
|
||||
case constant.TagObjectType:
|
||||
tagInfo, exist, err := os.tagRepo.GetTagByID(ctx, objectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
break
|
||||
}
|
||||
objInfo = &schema.UnreviewedRevisionInfoInfo{
|
||||
ObjectID: tagInfo.ID,
|
||||
Title: tagInfo.SlugName,
|
||||
Content: tagInfo.OriginalText,
|
||||
Html: tagInfo.ParsedText,
|
||||
}
|
||||
}
|
||||
if objInfo == nil {
|
||||
err = errors.BadRequest(reason.ObjectNotFound)
|
||||
}
|
||||
return objInfo, err
|
||||
}
|
||||
|
||||
// GetInfo get object simple information
|
||||
func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *schema.SimpleObjectInfo, err error) {
|
||||
|
@ -52,12 +131,12 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
|
|||
break
|
||||
}
|
||||
objInfo = &schema.SimpleObjectInfo{
|
||||
ObjectID: questionInfo.ID,
|
||||
ObjectCreator: questionInfo.UserID,
|
||||
QuestionID: questionInfo.ID,
|
||||
ObjectType: objectType,
|
||||
Title: questionInfo.Title,
|
||||
Content: questionInfo.ParsedText, // todo trim
|
||||
ObjectID: questionInfo.ID,
|
||||
ObjectCreatorUserID: questionInfo.UserID,
|
||||
QuestionID: questionInfo.ID,
|
||||
ObjectType: objectType,
|
||||
Title: questionInfo.Title,
|
||||
Content: questionInfo.ParsedText, // todo trim
|
||||
}
|
||||
case constant.AnswerObjectType:
|
||||
answerInfo, exist, err := os.answerRepo.GetAnswer(ctx, objectID)
|
||||
|
@ -72,13 +151,13 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
|
|||
return nil, err
|
||||
}
|
||||
objInfo = &schema.SimpleObjectInfo{
|
||||
ObjectID: answerInfo.ID,
|
||||
ObjectCreator: answerInfo.UserID,
|
||||
QuestionID: answerInfo.QuestionID,
|
||||
AnswerID: answerInfo.ID,
|
||||
ObjectType: objectType,
|
||||
Title: questionInfo.Title, // this should be question title
|
||||
Content: answerInfo.ParsedText, // todo trim
|
||||
ObjectID: answerInfo.ID,
|
||||
ObjectCreatorUserID: answerInfo.UserID,
|
||||
QuestionID: answerInfo.QuestionID,
|
||||
AnswerID: answerInfo.ID,
|
||||
ObjectType: objectType,
|
||||
Title: questionInfo.Title, // this should be question title
|
||||
Content: answerInfo.ParsedText, // todo trim
|
||||
}
|
||||
case constant.CommentObjectType:
|
||||
commentInfo, exist, err := os.commentRepo.GetComment(ctx, objectID)
|
||||
|
@ -89,11 +168,11 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
|
|||
break
|
||||
}
|
||||
objInfo = &schema.SimpleObjectInfo{
|
||||
ObjectID: commentInfo.ID,
|
||||
ObjectCreator: commentInfo.UserID,
|
||||
ObjectType: objectType,
|
||||
Content: commentInfo.ParsedText, // todo trim
|
||||
CommentID: commentInfo.ID,
|
||||
ObjectID: commentInfo.ID,
|
||||
ObjectCreatorUserID: commentInfo.UserID,
|
||||
ObjectType: objectType,
|
||||
Content: commentInfo.ParsedText, // todo trim
|
||||
CommentID: commentInfo.ID,
|
||||
}
|
||||
if len(commentInfo.QuestionID) > 0 {
|
||||
questionInfo, exist, err := os.questionRepo.GetQuestion(ctx, commentInfo.QuestionID)
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package permission
|
||||
|
||||
import "github.com/answerdev/answer/internal/schema"
|
||||
import (
|
||||
"context"
|
||||
|
||||
// TODO: There is currently no permission management
|
||||
func GetCommentPermission(userID string, commentCreatorUserID string) (
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
)
|
||||
|
||||
// GetCommentPermission get comment permission
|
||||
func GetCommentPermission(ctx context.Context, userID string, creatorUserID string, canEdit, canDelete bool) (
|
||||
actions []*schema.PermissionMemberAction) {
|
||||
actions = make([]*schema.PermissionMemberAction, 0)
|
||||
if len(userID) > 0 {
|
||||
|
@ -13,44 +17,48 @@ func GetCommentPermission(userID string, commentCreatorUserID string) (
|
|||
Type: "reason",
|
||||
})
|
||||
}
|
||||
if userID != commentCreatorUserID {
|
||||
return actions
|
||||
}
|
||||
actions = append(actions, []*schema.PermissionMemberAction{
|
||||
{
|
||||
if canEdit || userID == creatorUserID {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "edit",
|
||||
Name: "Edit",
|
||||
Type: "edit",
|
||||
},
|
||||
{
|
||||
})
|
||||
}
|
||||
|
||||
if canDelete || userID == creatorUserID {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "delete",
|
||||
Name: "Delete",
|
||||
Type: "reason",
|
||||
},
|
||||
}...)
|
||||
})
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func GetTagPermission(userID string, tagCreatorUserID string) (
|
||||
// GetTagPermission get tag permission
|
||||
func GetTagPermission(ctx context.Context, canEdit, canDelete bool) (
|
||||
actions []*schema.PermissionMemberAction) {
|
||||
if userID != tagCreatorUserID {
|
||||
return []*schema.PermissionMemberAction{}
|
||||
}
|
||||
return []*schema.PermissionMemberAction{
|
||||
{
|
||||
actions = make([]*schema.PermissionMemberAction, 0)
|
||||
if canEdit {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "edit",
|
||||
Name: "Edit",
|
||||
Type: "edit",
|
||||
},
|
||||
{
|
||||
})
|
||||
}
|
||||
|
||||
if canDelete {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "delete",
|
||||
Name: "Delete",
|
||||
Type: "reason",
|
||||
},
|
||||
})
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func GetAnswerPermission(userID string, answerAuthID string) (
|
||||
// GetAnswerPermission get answer permission
|
||||
func GetAnswerPermission(ctx context.Context, userID string, creatorUserID string, canEdit, canDelete bool) (
|
||||
actions []*schema.PermissionMemberAction) {
|
||||
actions = make([]*schema.PermissionMemberAction, 0)
|
||||
if len(userID) > 0 {
|
||||
|
@ -60,25 +68,26 @@ func GetAnswerPermission(userID string, answerAuthID string) (
|
|||
Type: "reason",
|
||||
})
|
||||
}
|
||||
if userID != answerAuthID {
|
||||
return actions
|
||||
}
|
||||
actions = append(actions, []*schema.PermissionMemberAction{
|
||||
{
|
||||
if canEdit || userID == creatorUserID {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "edit",
|
||||
Name: "Edit",
|
||||
Type: "edit",
|
||||
},
|
||||
{
|
||||
})
|
||||
}
|
||||
|
||||
if canDelete || userID == creatorUserID {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "delete",
|
||||
Name: "Delete",
|
||||
Type: "confirm",
|
||||
},
|
||||
}...)
|
||||
})
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func GetQuestionPermission(userID string, questionAuthID string) (
|
||||
// GetQuestionPermission get question permission
|
||||
func GetQuestionPermission(ctx context.Context, userID string, creatorUserID string, canEdit, canDelete, canClose bool) (
|
||||
actions []*schema.PermissionMemberAction) {
|
||||
actions = make([]*schema.PermissionMemberAction, 0)
|
||||
if len(userID) > 0 {
|
||||
|
@ -88,25 +97,40 @@ func GetQuestionPermission(userID string, questionAuthID string) (
|
|||
Type: "reason",
|
||||
})
|
||||
}
|
||||
if userID != questionAuthID {
|
||||
return actions
|
||||
}
|
||||
actions = append(actions, []*schema.PermissionMemberAction{
|
||||
{
|
||||
if canEdit || userID == creatorUserID {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "edit",
|
||||
Name: "Edit",
|
||||
Type: "edit",
|
||||
},
|
||||
{
|
||||
})
|
||||
}
|
||||
if canClose {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "close",
|
||||
Name: "Close",
|
||||
Type: "confirm",
|
||||
},
|
||||
{
|
||||
})
|
||||
}
|
||||
if canDelete || userID == creatorUserID {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "delete",
|
||||
Name: "Delete",
|
||||
Type: "confirm",
|
||||
},
|
||||
}...)
|
||||
})
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
// GetTagSynonymPermission get tag synonym permission
|
||||
func GetTagSynonymPermission(ctx context.Context, canEdit bool) (
|
||||
actions []*schema.PermissionMemberAction) {
|
||||
actions = make([]*schema.PermissionMemberAction, 0)
|
||||
if canEdit {
|
||||
actions = append(actions, &schema.PermissionMemberAction{
|
||||
Action: "edit",
|
||||
Name: "Edit",
|
||||
Type: "edit",
|
||||
})
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package service
|
|||
import (
|
||||
"github.com/answerdev/answer/internal/service/action"
|
||||
"github.com/answerdev/answer/internal/service/activity"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
answercommon "github.com/answerdev/answer/internal/service/answer_common"
|
||||
"github.com/answerdev/answer/internal/service/auth"
|
||||
collectioncommon "github.com/answerdev/answer/internal/service/collection_common"
|
||||
|
@ -22,6 +23,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/report_backyard"
|
||||
"github.com/answerdev/answer/internal/service/report_handle_backyard"
|
||||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
"github.com/answerdev/answer/internal/service/search_parser"
|
||||
"github.com/answerdev/answer/internal/service/role"
|
||||
"github.com/answerdev/answer/internal/service/search_parser"
|
||||
"github.com/answerdev/answer/internal/service/siteinfo"
|
||||
|
@ -73,6 +75,8 @@ var ProviderSetService = wire.NewSet(
|
|||
notification.NewNotificationService,
|
||||
activity.NewAnswerActivityService,
|
||||
dashboard.NewDashboardService,
|
||||
activity_common.NewActivityCommon,
|
||||
activity.NewActivityService,
|
||||
role.NewRoleService,
|
||||
role.NewUserRoleRelService,
|
||||
role.NewRolePowerRelService,
|
||||
|
|
|
@ -5,8 +5,10 @@ import (
|
|||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
"github.com/answerdev/answer/internal/service/config"
|
||||
"github.com/answerdev/answer/internal/service/meta"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
|
@ -232,6 +234,9 @@ func (qs *QuestionCommon) ListFormat(ctx context.Context, questionList []*entity
|
|||
list = append(list, item)
|
||||
objectIds = append(objectIds, item.ID)
|
||||
userIds = append(userIds, questionInfo.UserID)
|
||||
userIds = append(userIds, questionInfo.LastEditUserID)
|
||||
userIds = append(userIds, item.LastAnsweredUserID)
|
||||
|
||||
}
|
||||
tagsMap, err := qs.tagCommon.BatchGetObjectTag(ctx, objectIds)
|
||||
if err != nil {
|
||||
|
@ -252,7 +257,14 @@ func (qs *QuestionCommon) ListFormat(ctx context.Context, questionList []*entity
|
|||
if ok {
|
||||
item.UserInfo = userInfoMap[item.UserID]
|
||||
item.UpdateUserInfo = userInfoMap[item.UserID]
|
||||
item.LastAnsweredUserInfo = userInfoMap[item.UserID]
|
||||
}
|
||||
_, ok = userInfoMap[item.LastEditUserID]
|
||||
if ok {
|
||||
item.UpdateUserInfo = userInfoMap[item.UserID]
|
||||
}
|
||||
_, ok = userInfoMap[item.LastAnsweredUserID]
|
||||
if ok {
|
||||
item.LastAnsweredUserInfo = userInfoMap[item.LastAnsweredUserID]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,7 +320,7 @@ func (qs *QuestionCommon) CloseQuestion(ctx context.Context, req *schema.CloseQu
|
|||
if !has {
|
||||
return nil
|
||||
}
|
||||
questionInfo.Status = entity.QuestionStatusclosed
|
||||
questionInfo.Status = entity.QuestionStatusClosed
|
||||
err = qs.questionRepo.UpdateQuestionStatus(ctx, questionInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -322,6 +334,13 @@ func (qs *QuestionCommon) CloseQuestion(ctx context.Context, req *schema.CloseQu
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: questionInfo.UserID,
|
||||
ObjectID: questionInfo.ID,
|
||||
OriginalObjectID: questionInfo.ID,
|
||||
ActivityTypeKey: constant.ActQuestionClosed,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -371,9 +390,41 @@ func (qs *QuestionCommon) ShowFormat(ctx context.Context, data *entity.Question)
|
|||
info.CreateTime = data.CreatedAt.Unix()
|
||||
info.UpdateTime = data.UpdatedAt.Unix()
|
||||
info.PostUpdateTime = data.PostUpdateTime.Unix()
|
||||
if data.PostUpdateTime.Unix() < 1 {
|
||||
info.PostUpdateTime = 0
|
||||
}
|
||||
info.QuestionUpdateTime = data.UpdatedAt.Unix()
|
||||
if data.UpdatedAt.Unix() < 1 {
|
||||
info.QuestionUpdateTime = 0
|
||||
}
|
||||
info.Status = data.Status
|
||||
info.UserID = data.UserID
|
||||
info.LastEditUserID = data.LastEditUserID
|
||||
if data.LastAnswerID != "0" {
|
||||
answerInfo, exist, err := qs.answerRepo.GetAnswer(ctx, data.LastAnswerID)
|
||||
if err == nil && exist {
|
||||
if answerInfo.LastEditUserID != "0" {
|
||||
info.LastAnsweredUserID = answerInfo.LastEditUserID
|
||||
} else {
|
||||
info.LastAnsweredUserID = answerInfo.UserID
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
info.Tags = make([]*schema.TagResp, 0)
|
||||
return &info
|
||||
}
|
||||
func (qs *QuestionCommon) ShowFormatWithTag(ctx context.Context, data *entity.QuestionWithTagsRevision) *schema.QuestionInfo {
|
||||
info := qs.ShowFormat(ctx, &data.Question)
|
||||
Tags := make([]*schema.TagResp, 0)
|
||||
for _, tag := range data.Tags {
|
||||
item := &schema.TagResp{}
|
||||
item.SlugName = tag.SlugName
|
||||
item.DisplayName = tag.DisplayName
|
||||
item.Recommend = tag.Recommend
|
||||
item.Reserved = tag.Reserved
|
||||
Tags = append(Tags, item)
|
||||
}
|
||||
info.Tags = Tags
|
||||
return info
|
||||
}
|
||||
|
|
|
@ -7,11 +7,14 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/base/translator"
|
||||
"github.com/answerdev/answer/internal/base/validator"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/activity"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
collectioncommon "github.com/answerdev/answer/internal/service/collection_common"
|
||||
"github.com/answerdev/answer/internal/service/meta"
|
||||
"github.com/answerdev/answer/internal/service/notice_queue"
|
||||
|
@ -71,7 +74,13 @@ func (qs *QuestionService) CloseQuestion(ctx context.Context, req *schema.CloseQ
|
|||
if !has {
|
||||
return nil
|
||||
}
|
||||
questionInfo.Status = entity.QuestionStatusclosed
|
||||
|
||||
if !req.IsAdmin {
|
||||
if questionInfo.UserID != req.UserID {
|
||||
return errors.BadRequest(reason.QuestionCannotClose)
|
||||
}
|
||||
}
|
||||
questionInfo.Status = entity.QuestionStatusClosed
|
||||
err = qs.questionRepo.UpdateQuestionStatus(ctx, questionInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -85,6 +94,13 @@ func (qs *QuestionService) CloseQuestion(ctx context.Context, req *schema.CloseQ
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: req.UserID,
|
||||
ObjectID: questionInfo.ID,
|
||||
OriginalObjectID: questionInfo.ID,
|
||||
ActivityTypeKey: constant.ActQuestionClosed,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -105,18 +121,21 @@ func (qs *QuestionService) CloseMsgList(ctx context.Context, lang i18n.Language)
|
|||
}
|
||||
|
||||
// AddQuestion add question
|
||||
func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.QuestionAdd) (questionInfo *schema.QuestionInfo, err error) {
|
||||
func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.QuestionAdd) (questionInfo any, err error) {
|
||||
recommendExist, err := qs.tagCommon.ExistRecommend(ctx, req.Tags)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !recommendExist {
|
||||
err = fmt.Errorf("recommend is not exist")
|
||||
err = errors.BadRequest(reason.RecommendTagNotExist).WithError(err).WithStack()
|
||||
return
|
||||
errorlist := make([]*validator.FormErrorField, 0)
|
||||
errorlist = append(errorlist, &validator.FormErrorField{
|
||||
ErrorField: "tags",
|
||||
ErrorMsg: translator.GlobalTrans.Tr(handler.GetLangByCtx(ctx), reason.RecommendTagEnter),
|
||||
})
|
||||
err = errors.BadRequest(reason.RecommendTagEnter)
|
||||
return errorlist, err
|
||||
}
|
||||
|
||||
questionInfo = &schema.QuestionInfo{}
|
||||
question := &entity.Question{}
|
||||
now := time.Now()
|
||||
question.UserID = req.UserID
|
||||
|
@ -125,11 +144,11 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
|
|||
question.ParsedText = req.HTML
|
||||
question.AcceptedAnswerID = "0"
|
||||
question.LastAnswerID = "0"
|
||||
question.PostUpdateTime = now
|
||||
//question.PostUpdateTime = nil
|
||||
question.Status = entity.QuestionStatusAvailable
|
||||
question.RevisionID = "0"
|
||||
question.CreatedAt = now
|
||||
question.UpdatedAt = now
|
||||
//question.UpdatedAt = nil
|
||||
err = qs.questionRepo.AddQuestion(ctx, question)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -146,11 +165,25 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
|
|||
revisionDTO := &schema.AddRevisionDTO{
|
||||
UserID: question.UserID,
|
||||
ObjectID: question.ID,
|
||||
Title: "",
|
||||
Title: question.Title,
|
||||
}
|
||||
infoJSON, _ := json.Marshal(question)
|
||||
|
||||
tagNameList := make([]string, 0)
|
||||
for _, tag := range req.Tags {
|
||||
tagNameList = append(tagNameList, tag.SlugName)
|
||||
}
|
||||
Tags, tagerr := qs.tagCommon.GetTagListByNames(ctx, tagNameList)
|
||||
if tagerr != nil {
|
||||
return questionInfo, tagerr
|
||||
}
|
||||
|
||||
questionWithTagsRevision, err := qs.changeQuestionToRevision(ctx, question, Tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infoJSON, _ := json.Marshal(questionWithTagsRevision)
|
||||
revisionDTO.Content = string(infoJSON)
|
||||
err = qs.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
revisionID, err := qs.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -161,7 +194,15 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
|
|||
log.Error("user IncreaseQuestionCount error", err.Error())
|
||||
}
|
||||
|
||||
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, false)
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: question.UserID,
|
||||
ObjectID: question.ID,
|
||||
OriginalObjectID: question.ID,
|
||||
ActivityTypeKey: constant.ActQuestionAsked,
|
||||
RevisionID: revisionID,
|
||||
})
|
||||
|
||||
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, req.QuestionPermission)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -174,15 +215,31 @@ func (qs *QuestionService) RemoveQuestion(ctx context.Context, req *schema.Remov
|
|||
if !has {
|
||||
return nil
|
||||
}
|
||||
if questionInfo.UserID != req.UserID {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
}
|
||||
if !req.IsAdmin {
|
||||
if questionInfo.UserID != req.UserID {
|
||||
return errors.BadRequest(reason.QuestionCannotDeleted)
|
||||
}
|
||||
|
||||
if questionInfo.AcceptedAnswerID != "" {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
}
|
||||
if questionInfo.AnswerCount > 0 {
|
||||
return errors.BadRequest(reason.UnauthorizedError)
|
||||
if questionInfo.AcceptedAnswerID != "0" {
|
||||
return errors.BadRequest(reason.QuestionCannotDeleted)
|
||||
}
|
||||
if questionInfo.AnswerCount > 1 {
|
||||
return errors.BadRequest(reason.QuestionCannotDeleted)
|
||||
}
|
||||
|
||||
if questionInfo.AnswerCount == 1 {
|
||||
answersearch := &entity.AnswerSearch{}
|
||||
answersearch.QuestionID = req.ID
|
||||
answerList, _, err := qs.questioncommon.AnswerCommon.Search(ctx, answersearch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, answer := range answerList {
|
||||
if answer.VoteCount > 0 {
|
||||
return errors.BadRequest(reason.QuestionCannotDeleted)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
questionInfo.Status = entity.QuestionStatusDeleted
|
||||
|
@ -201,106 +258,198 @@ func (qs *QuestionService) RemoveQuestion(ctx context.Context, req *schema.Remov
|
|||
if err != nil {
|
||||
log.Errorf("user DeleteQuestion rank rollback error %s", err.Error())
|
||||
}
|
||||
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: questionInfo.UserID,
|
||||
ObjectID: questionInfo.ID,
|
||||
OriginalObjectID: questionInfo.ID,
|
||||
ActivityTypeKey: constant.ActQuestionDeleted,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateQuestion update question
|
||||
func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.QuestionUpdate) (questionInfo *schema.QuestionInfo, err error) {
|
||||
func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.QuestionUpdate) (questionInfo any, err error) {
|
||||
var canUpdate bool
|
||||
questionInfo = &schema.QuestionInfo{}
|
||||
now := time.Now()
|
||||
question := &entity.Question{}
|
||||
question.UserID = req.UserID
|
||||
question.Title = req.Title
|
||||
question.OriginalText = req.Content
|
||||
question.ParsedText = req.HTML
|
||||
question.ID = req.ID
|
||||
question.UpdatedAt = now
|
||||
dbinfo, has, err := qs.questionRepo.GetQuestion(ctx, question.ID)
|
||||
|
||||
_, existUnreviewed, err := qs.revisionService.ExistUnreviewedByObjectID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
}
|
||||
if existUnreviewed {
|
||||
err = errors.BadRequest(reason.QuestionCannotUpdate)
|
||||
return
|
||||
}
|
||||
|
||||
dbinfo, has, err := qs.questionRepo.GetQuestion(ctx, req.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !has {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
question := &entity.Question{}
|
||||
question.Title = req.Title
|
||||
question.OriginalText = req.Content
|
||||
question.ParsedText = req.HTML
|
||||
question.ID = req.ID
|
||||
question.UpdatedAt = now
|
||||
question.PostUpdateTime = now
|
||||
question.UserID = dbinfo.UserID
|
||||
|
||||
question.LastEditUserID = "0"
|
||||
if dbinfo.UserID != req.UserID {
|
||||
return
|
||||
question.LastEditUserID = req.UserID
|
||||
}
|
||||
|
||||
//CheckChangeTag
|
||||
oldTags, err := qs.tagCommon.GetObjectEntityTag(ctx, question.ID)
|
||||
if err != nil {
|
||||
return
|
||||
oldTags, tagerr := qs.tagCommon.GetObjectEntityTag(ctx, question.ID)
|
||||
if tagerr != nil {
|
||||
return questionInfo, tagerr
|
||||
}
|
||||
|
||||
tagNameList := make([]string, 0)
|
||||
oldtagNameList := make([]string, 0)
|
||||
for _, tag := range req.Tags {
|
||||
tagNameList = append(tagNameList, tag.SlugName)
|
||||
}
|
||||
Tags, err := qs.tagCommon.GetTagListByNames(ctx, tagNameList)
|
||||
if err != nil {
|
||||
return
|
||||
for _, tag := range oldTags {
|
||||
oldtagNameList = append(oldtagNameList, tag.SlugName)
|
||||
}
|
||||
CheckTag, CheckTaglist := qs.CheckChangeTag(ctx, oldTags, Tags)
|
||||
if !CheckTag {
|
||||
err = errors.BadRequest(reason.UnauthorizedError).WithMsg(fmt.Sprintf("tag [%s] cannot be modified",
|
||||
strings.Join(CheckTaglist, ",")))
|
||||
|
||||
isChange := qs.tagCommon.CheckTagsIsChange(ctx, tagNameList, oldtagNameList)
|
||||
|
||||
//If the content is the same, ignore it
|
||||
if dbinfo.Title == req.Title && dbinfo.OriginalText == req.Content && !isChange {
|
||||
return
|
||||
}
|
||||
|
||||
//update question to db
|
||||
err = qs.questionRepo.UpdateQuestion(ctx, question, []string{"title", "original_text", "parsed_text", "updated_at"})
|
||||
Tags, tagerr := qs.tagCommon.GetTagListByNames(ctx, tagNameList)
|
||||
if tagerr != nil {
|
||||
return questionInfo, tagerr
|
||||
}
|
||||
|
||||
// If it's not admin
|
||||
if !req.IsAdmin {
|
||||
//CheckChangeTag
|
||||
|
||||
CheckTag, CheckTaglist := qs.CheckChangeReservedTag(ctx, oldTags, Tags)
|
||||
if !CheckTag {
|
||||
errMsg := fmt.Sprintf(`The reserved tag "%s" must be present.`,
|
||||
strings.Join(CheckTaglist, ","))
|
||||
errorlist := make([]*validator.FormErrorField, 0)
|
||||
errorlist = append(errorlist, &validator.FormErrorField{
|
||||
ErrorField: "tags",
|
||||
ErrorMsg: errMsg,
|
||||
})
|
||||
err = errors.BadRequest(reason.RequestFormatError).WithMsg(errMsg)
|
||||
return errorlist, err
|
||||
}
|
||||
}
|
||||
// Check whether mandatory labels are selected
|
||||
recommendExist, err := qs.tagCommon.ExistRecommend(ctx, req.Tags)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
objectTagData := schema.TagChange{}
|
||||
objectTagData.ObjectID = question.ID
|
||||
objectTagData.Tags = req.Tags
|
||||
objectTagData.UserID = req.UserID
|
||||
err = qs.ChangeTag(ctx, &objectTagData)
|
||||
if err != nil {
|
||||
return
|
||||
if !recommendExist {
|
||||
errorlist := make([]*validator.FormErrorField, 0)
|
||||
errorlist = append(errorlist, &validator.FormErrorField{
|
||||
ErrorField: "tags",
|
||||
ErrorMsg: translator.GlobalTrans.Tr(handler.GetLangByCtx(ctx), reason.RecommendTagEnter),
|
||||
})
|
||||
err = errors.BadRequest(reason.RecommendTagEnter)
|
||||
return errorlist, err
|
||||
}
|
||||
|
||||
//Administrators and themselves do not need to be audited
|
||||
|
||||
revisionDTO := &schema.AddRevisionDTO{
|
||||
UserID: question.UserID,
|
||||
ObjectID: question.ID,
|
||||
Title: "",
|
||||
Title: question.Title,
|
||||
Log: req.EditSummary,
|
||||
}
|
||||
infoJSON, _ := json.Marshal(question)
|
||||
|
||||
if req.NoNeedReview || req.IsAdmin || dbinfo.UserID == req.UserID {
|
||||
canUpdate = true
|
||||
}
|
||||
|
||||
// It's not you or the administrator that needs to be reviewed
|
||||
if !canUpdate {
|
||||
revisionDTO.Status = entity.RevisionUnreviewedStatus
|
||||
} else {
|
||||
//Direct modification
|
||||
revisionDTO.Status = entity.RevisionReviewPassStatus
|
||||
//update question to db
|
||||
saveerr := qs.questionRepo.UpdateQuestion(ctx, question, []string{"title", "original_text", "parsed_text", "updated_at", "post_update_time", "last_edit_user_id"})
|
||||
if saveerr != nil {
|
||||
return questionInfo, saveerr
|
||||
}
|
||||
objectTagData := schema.TagChange{}
|
||||
objectTagData.ObjectID = question.ID
|
||||
objectTagData.Tags = req.Tags
|
||||
objectTagData.UserID = req.UserID
|
||||
tagerr := qs.ChangeTag(ctx, &objectTagData)
|
||||
if err != nil {
|
||||
return questionInfo, tagerr
|
||||
}
|
||||
}
|
||||
|
||||
questionWithTagsRevision, err := qs.changeQuestionToRevision(ctx, question, Tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infoJSON, _ := json.Marshal(questionWithTagsRevision)
|
||||
revisionDTO.Content = string(infoJSON)
|
||||
err = qs.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
revisionID, err := qs.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if canUpdate {
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: req.UserID,
|
||||
ObjectID: question.ID,
|
||||
ActivityTypeKey: constant.ActQuestionEdited,
|
||||
RevisionID: revisionID,
|
||||
OriginalObjectID: question.ID,
|
||||
})
|
||||
}
|
||||
|
||||
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, false)
|
||||
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, req.QuestionPermission)
|
||||
return
|
||||
}
|
||||
|
||||
// GetQuestion get question one
|
||||
func (qs *QuestionService) GetQuestion(ctx context.Context, id, loginUserID string, addpv bool) (resp *schema.QuestionInfo, err error) {
|
||||
question, err := qs.questioncommon.Info(ctx, id, loginUserID)
|
||||
func (qs *QuestionService) GetQuestion(ctx context.Context, questionID, userID string,
|
||||
per schema.QuestionPermission) (resp *schema.QuestionInfo, err error) {
|
||||
question, err := qs.questioncommon.Info(ctx, questionID, userID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if addpv {
|
||||
err = qs.questioncommon.UpdataPv(ctx, id)
|
||||
if err != nil {
|
||||
log.Error("UpdataPv", err)
|
||||
}
|
||||
}
|
||||
|
||||
question.MemberActions = permission.GetQuestionPermission(loginUserID, question.UserID)
|
||||
question.MemberActions = permission.GetQuestionPermission(ctx, userID, question.UserID,
|
||||
per.CanEdit, per.CanDelete, per.CanClose)
|
||||
return question, nil
|
||||
}
|
||||
|
||||
// GetQuestionAndAddPV get question one
|
||||
func (qs *QuestionService) GetQuestionAndAddPV(ctx context.Context, questionID, loginUserID string,
|
||||
per schema.QuestionPermission) (
|
||||
resp *schema.QuestionInfo, err error) {
|
||||
err = qs.questioncommon.UpdataPv(ctx, questionID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return qs.GetQuestion(ctx, questionID, loginUserID, per)
|
||||
}
|
||||
|
||||
func (qs *QuestionService) ChangeTag(ctx context.Context, objectTagData *schema.TagChange) error {
|
||||
return qs.tagCommon.ObjectChangeTag(ctx, objectTagData)
|
||||
}
|
||||
|
||||
func (qs *QuestionService) CheckChangeTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, []string) {
|
||||
return qs.tagCommon.ObjectCheckChangeTag(ctx, oldobjectTagData, objectTagData)
|
||||
func (qs *QuestionService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, []string) {
|
||||
return qs.tagCommon.CheckChangeReservedTag(ctx, oldobjectTagData, objectTagData)
|
||||
}
|
||||
|
||||
func (qs *QuestionService) SearchUserList(ctx context.Context, userName, order string, page, pageSize int, loginUserID string) ([]*schema.UserQuestionInfo, int64, error) {
|
||||
|
@ -518,12 +667,12 @@ func (qs *QuestionService) SearchByTitleLike(ctx context.Context, title string,
|
|||
// SimilarQuestion
|
||||
func (qs *QuestionService) SimilarQuestion(ctx context.Context, questionID string, loginUserID string) ([]*schema.QuestionInfo, int64, error) {
|
||||
list := make([]*schema.QuestionInfo, 0)
|
||||
questionInfo, err := qs.GetQuestion(ctx, questionID, loginUserID, false)
|
||||
question, err := qs.questioncommon.Info(ctx, questionID, loginUserID)
|
||||
if err != nil {
|
||||
return list, 0, err
|
||||
return list, 0, nil
|
||||
}
|
||||
tagNames := make([]string, 0, len(questionInfo.Tags))
|
||||
for _, tag := range questionInfo.Tags {
|
||||
tagNames := make([]string, 0, len(question.Tags))
|
||||
for _, tag := range question.Tags {
|
||||
tagNames = append(tagNames, tag.SlugName)
|
||||
}
|
||||
search := &schema.QuestionSearch{}
|
||||
|
@ -581,8 +730,7 @@ func (qs *QuestionService) AdminSetQuestionStatus(ctx context.Context, questionI
|
|||
if !exist {
|
||||
return errors.BadRequest(reason.QuestionNotFound)
|
||||
}
|
||||
questionInfo.Status = setStatus
|
||||
err = qs.questionRepo.UpdateQuestionStatus(ctx, questionInfo)
|
||||
err = qs.questionRepo.UpdateQuestionStatus(ctx, &entity.Question{ID: questionInfo.ID, Status: setStatus})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -593,6 +741,22 @@ func (qs *QuestionService) AdminSetQuestionStatus(ctx context.Context, questionI
|
|||
log.Errorf("admin delete question then rank rollback error %s", err.Error())
|
||||
}
|
||||
}
|
||||
if setStatus == entity.QuestionStatusAvailable && questionInfo.Status == entity.QuestionStatusClosed {
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: questionInfo.UserID,
|
||||
ObjectID: questionInfo.ID,
|
||||
OriginalObjectID: questionInfo.ID,
|
||||
ActivityTypeKey: constant.ActQuestionDeleted,
|
||||
})
|
||||
}
|
||||
if setStatus == entity.QuestionStatusClosed && questionInfo.Status != entity.QuestionStatusClosed {
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: questionInfo.UserID,
|
||||
ObjectID: questionInfo.ID,
|
||||
OriginalObjectID: questionInfo.ID,
|
||||
ActivityTypeKey: constant.ActQuestionClosed,
|
||||
})
|
||||
}
|
||||
msg := &schema.NotificationMsg{}
|
||||
msg.ObjectID = questionInfo.ID
|
||||
msg.Type = schema.NotificationTypeInbox
|
||||
|
@ -688,3 +852,16 @@ func (qs *QuestionService) CmsSearchAnswerList(ctx context.Context, search *enti
|
|||
}
|
||||
return answerlist, count, nil
|
||||
}
|
||||
|
||||
func (qs *QuestionService) changeQuestionToRevision(ctx context.Context, questionInfo *entity.Question, tags []*entity.Tag) (
|
||||
questionRevision *entity.QuestionWithTagsRevision, err error) {
|
||||
questionRevision = &entity.QuestionWithTagsRevision{}
|
||||
questionRevision.Question = *questionInfo
|
||||
|
||||
for _, tag := range tags {
|
||||
item := &entity.TagSimpleInfoForRevision{}
|
||||
_ = copier.Copy(item, tag)
|
||||
questionRevision.Tags = append(questionRevision.Tags, item)
|
||||
}
|
||||
return questionRevision, nil
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package rank
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/pager"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
|
@ -17,27 +18,35 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
QuestionAddRank = "rank.question.add"
|
||||
QuestionEditRank = "rank.question.edit"
|
||||
QuestionDeleteRank = "rank.question.delete"
|
||||
QuestionVoteUpRank = "rank.question.vote_up"
|
||||
QuestionVoteDownRank = "rank.question.vote_down"
|
||||
AnswerAddRank = "rank.answer.add"
|
||||
AnswerEditRank = "rank.answer.edit"
|
||||
AnswerDeleteRank = "rank.answer.delete"
|
||||
AnswerAcceptRank = "rank.answer.accept"
|
||||
AnswerVoteUpRank = "rank.answer.vote_up"
|
||||
AnswerVoteDownRank = "rank.answer.vote_down"
|
||||
CommentAddRank = "rank.comment.add"
|
||||
CommentEditRank = "rank.comment.edit"
|
||||
CommentDeleteRank = "rank.comment.delete"
|
||||
ReportAddRank = "rank.report.add"
|
||||
TagAddRank = "rank.tag.add"
|
||||
TagEditRank = "rank.tag.edit"
|
||||
TagDeleteRank = "rank.tag.delete"
|
||||
TagSynonymRank = "rank.tag.synonym"
|
||||
LinkUrlLimitRank = "rank.link.url_limit"
|
||||
VoteDetailRank = "rank.vote.detail"
|
||||
QuestionAddRank = "rank.question.add"
|
||||
QuestionEditRank = "rank.question.edit"
|
||||
QuestionEditWithoutReviewRank = "rank.question.edit_without_review"
|
||||
QuestionDeleteRank = "rank.question.delete"
|
||||
QuestionVoteUpRank = "rank.question.vote_up"
|
||||
QuestionVoteDownRank = "rank.question.vote_down"
|
||||
AnswerAddRank = "rank.answer.add"
|
||||
AnswerEditRank = "rank.answer.edit"
|
||||
AnswerEditWithoutReviewRank = "rank.answer.edit_without_review"
|
||||
AnswerDeleteRank = "rank.answer.delete"
|
||||
AnswerAcceptRank = "rank.answer.accept"
|
||||
AnswerVoteUpRank = "rank.answer.vote_up"
|
||||
AnswerVoteDownRank = "rank.answer.vote_down"
|
||||
CommentAddRank = "rank.comment.add"
|
||||
CommentEditRank = "rank.comment.edit"
|
||||
CommentDeleteRank = "rank.comment.delete"
|
||||
CommentVoteUpRank = "rank.comment.vote_up"
|
||||
CommentVoteDownRank = "rank.comment.vote_down"
|
||||
ReportAddRank = "rank.report.add"
|
||||
TagAddRank = "rank.tag.add"
|
||||
TagEditRank = "rank.tag.edit"
|
||||
TagEditWithoutReviewRank = "rank.tag.edit_without_review"
|
||||
TagDeleteRank = "rank.tag.delete"
|
||||
TagSynonymRank = "rank.tag.synonym"
|
||||
LinkUrlLimitRank = "rank.link.url_limit"
|
||||
VoteDetailRank = "rank.vote.detail"
|
||||
AnswerAuditRank = "rank.answer.audit"
|
||||
QuestionAuditRank = "rank.question.audit"
|
||||
TagAuditRank = "rank.tag.audit"
|
||||
)
|
||||
|
||||
type UserRankRepo interface {
|
||||
|
@ -67,8 +76,9 @@ func NewRankService(
|
|||
}
|
||||
}
|
||||
|
||||
// CheckRankPermission check whether the user reputation meets the permission
|
||||
func (rs *RankService) CheckRankPermission(ctx context.Context, userID string, action string) (can bool, err error) {
|
||||
// CheckOperationPermission verify that the user has permission
|
||||
func (rs *RankService) CheckOperationPermission(ctx context.Context, userID string, action string, objectID string) (
|
||||
can bool, err error) {
|
||||
if len(userID) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -81,17 +91,130 @@ func (rs *RankService) CheckRankPermission(ctx context.Context, userID string, a
|
|||
if !exist {
|
||||
return false, nil
|
||||
}
|
||||
currentUserRank := userInfo.Rank
|
||||
// administrator have all permissions
|
||||
if userInfo.IsAdmin {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if len(objectID) > 0 {
|
||||
objectInfo, err := rs.objectInfoService.GetInfo(ctx, objectID)
|
||||
if err != nil {
|
||||
return can, err
|
||||
}
|
||||
// if the user is this object creator, the user can operate this object.
|
||||
if objectInfo != nil &&
|
||||
objectInfo.ObjectCreatorUserID == userID {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return rs.checkUserRank(ctx, userInfo.ID, userInfo.Rank, action)
|
||||
}
|
||||
|
||||
// CheckOperationPermissions verify that the user has permission
|
||||
func (rs *RankService) CheckOperationPermissions(ctx context.Context, userID string, actions []string, objectID string) (
|
||||
can []bool, err error) {
|
||||
can = make([]bool, len(actions))
|
||||
if len(userID) == 0 {
|
||||
return can, nil
|
||||
}
|
||||
|
||||
// get the rank of the current user
|
||||
userInfo, exist, err := rs.userCommon.GetUserBasicInfoByID(ctx, userID)
|
||||
if err != nil {
|
||||
return can, err
|
||||
}
|
||||
if !exist {
|
||||
return can, nil
|
||||
}
|
||||
|
||||
objectOwner := false
|
||||
if len(objectID) > 0 {
|
||||
objectInfo, err := rs.objectInfoService.GetInfo(ctx, objectID)
|
||||
if err != nil {
|
||||
return can, err
|
||||
}
|
||||
// if the user is this object creator, the user can operate this object.
|
||||
if objectInfo != nil &&
|
||||
objectInfo.ObjectCreatorUserID == userID {
|
||||
objectOwner = true
|
||||
}
|
||||
}
|
||||
|
||||
for idx, action := range actions {
|
||||
if userInfo.IsAdmin || objectOwner {
|
||||
can[idx] = true
|
||||
continue
|
||||
}
|
||||
meetRank, err := rs.checkUserRank(ctx, userInfo.ID, userInfo.Rank, action)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
can[idx] = meetRank
|
||||
}
|
||||
return can, nil
|
||||
}
|
||||
|
||||
// CheckVotePermission verify that the user has vote permission
|
||||
func (rs *RankService) CheckVotePermission(ctx context.Context, userID, objectID string, voteUp bool) (
|
||||
can bool, err error) {
|
||||
if len(userID) == 0 || len(objectID) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// get the rank of the current user
|
||||
userInfo, exist, err := rs.userCommon.GetUserBasicInfoByID(ctx, userID)
|
||||
if err != nil {
|
||||
return can, err
|
||||
}
|
||||
if !exist {
|
||||
return can, nil
|
||||
}
|
||||
|
||||
objectInfo, err := rs.objectInfoService.GetInfo(ctx, objectID)
|
||||
if err != nil {
|
||||
return can, err
|
||||
}
|
||||
|
||||
action := ""
|
||||
switch objectInfo.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
if voteUp {
|
||||
action = QuestionVoteUpRank
|
||||
} else {
|
||||
action = QuestionVoteDownRank
|
||||
}
|
||||
case constant.AnswerObjectType:
|
||||
if voteUp {
|
||||
action = AnswerVoteUpRank
|
||||
} else {
|
||||
action = AnswerVoteDownRank
|
||||
}
|
||||
case constant.CommentObjectType:
|
||||
if voteUp {
|
||||
action = CommentVoteUpRank
|
||||
} else {
|
||||
action = CommentVoteDownRank
|
||||
}
|
||||
}
|
||||
meetRank, err := rs.checkUserRank(ctx, userInfo.ID, userInfo.Rank, action)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return meetRank, nil
|
||||
}
|
||||
|
||||
// CheckRankPermission verify that the user meets the prestige criteria
|
||||
func (rs *RankService) checkUserRank(ctx context.Context, userID string, userRank int, action string) (
|
||||
can bool, err error) {
|
||||
// get the amount of rank required for the current operation
|
||||
requireRank, err := rs.configRepo.GetInt(action)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if currentUserRank < requireRank {
|
||||
if userRank < requireRank || requireRank < 0 {
|
||||
log.Debugf("user %s want to do action %s, but rank %d < %d",
|
||||
userInfo.DisplayName, action, currentUserRank, requireRank)
|
||||
userID, action, userRank, requireRank)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
|
|
|
@ -47,7 +47,7 @@ func (rs *ReportService) AddReport(ctx context.Context, req *schema.AddReportReq
|
|||
|
||||
report := &entity.Report{
|
||||
UserID: req.UserID,
|
||||
ReportedUserID: objInfo.ObjectCreator,
|
||||
ReportedUserID: objInfo.ObjectCreatorUserID,
|
||||
ObjectID: req.ObjectID,
|
||||
ObjectType: objectTypeNumber,
|
||||
ReportType: req.ReportType,
|
||||
|
|
|
@ -2,6 +2,7 @@ package revision
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
@ -9,7 +10,11 @@ import (
|
|||
// RevisionRepo revision repository
|
||||
type RevisionRepo interface {
|
||||
AddRevision(ctx context.Context, revision *entity.Revision, autoUpdateRevisionID bool) (err error)
|
||||
GetRevisionByID(ctx context.Context, revisionID string) (revision *entity.Revision, exist bool, err error)
|
||||
GetLastRevisionByObjectID(ctx context.Context, objectID string) (revision *entity.Revision, exist bool, err error)
|
||||
GetRevisionList(ctx context.Context, revision *entity.Revision) (revisionList []entity.Revision, err error)
|
||||
UpdateObjectRevisionId(ctx context.Context, revision *entity.Revision, session *xorm.Session) (err error)
|
||||
ExistUnreviewedByObjectID(ctx context.Context, objectID string) (revision *entity.Revision, exist bool, err error)
|
||||
GetUnreviewedRevisionPage(ctx context.Context, page, pageSize int, objectTypes []int) ([]*entity.Revision, int64, error)
|
||||
UpdateStatus(ctx context.Context, id string, status int) (err error)
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ package revision_common
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/service/revision"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
|
@ -24,17 +26,45 @@ func NewRevisionService(revisionRepo revision.RevisionRepo, userRepo usercommon.
|
|||
}
|
||||
}
|
||||
|
||||
func (rs *RevisionService) GetUnreviewedRevisionCount(ctx context.Context, req *schema.RevisionSearch) (count int64, err error) {
|
||||
if len(req.GetCanReviewObjectTypes()) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
_, count, err = rs.revisionRepo.GetUnreviewedRevisionPage(ctx, req.Page, 1, req.GetCanReviewObjectTypes())
|
||||
return count, err
|
||||
}
|
||||
|
||||
// AddRevision add revision
|
||||
//
|
||||
// autoUpdateRevisionID bool : if autoUpdateRevisionID is true , the object.revision_id will be updated,
|
||||
// if not need auto update object.revision_id, it must be false.
|
||||
// example: user can edit the object, but need audit, the revision_id will be updated when admin approved
|
||||
func (rs *RevisionService) AddRevision(ctx context.Context, req *schema.AddRevisionDTO, autoUpdateRevisionID bool) (err error) {
|
||||
func (rs *RevisionService) AddRevision(ctx context.Context, req *schema.AddRevisionDTO, autoUpdateRevisionID bool) (
|
||||
revisionID string, err error) {
|
||||
rev := &entity.Revision{}
|
||||
_ = copier.Copy(rev, req)
|
||||
err = rs.revisionRepo.AddRevision(ctx, rev, autoUpdateRevisionID)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
return nil
|
||||
return rev.ID, nil
|
||||
}
|
||||
|
||||
// GetRevision get revision
|
||||
func (rs *RevisionService) GetRevision(ctx context.Context, revisionID string) (
|
||||
revision *entity.Revision, err error) {
|
||||
revisionInfo, exist, err := rs.revisionRepo.GetRevisionByID(ctx, revisionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
return nil, errors.BadRequest(reason.ObjectNotFound)
|
||||
}
|
||||
return revisionInfo, nil
|
||||
}
|
||||
|
||||
// ExistUnreviewedByObjectID
|
||||
func (rs *RevisionService) ExistUnreviewedByObjectID(ctx context.Context, objectID string) (revision *entity.Revision, exist bool, err error) {
|
||||
revision, exist, err = rs.revisionRepo.ExistUnreviewedByObjectID(ctx, objectID)
|
||||
return revision, exist, err
|
||||
}
|
||||
|
|
|
@ -3,37 +3,296 @@ package service
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/pager"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
answercommon "github.com/answerdev/answer/internal/service/answer_common"
|
||||
"github.com/answerdev/answer/internal/service/notice_queue"
|
||||
"github.com/answerdev/answer/internal/service/object_info"
|
||||
questioncommon "github.com/answerdev/answer/internal/service/question_common"
|
||||
"github.com/answerdev/answer/internal/service/revision"
|
||||
"github.com/answerdev/answer/internal/service/tag_common"
|
||||
tagcommon "github.com/answerdev/answer/internal/service/tag_common"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
"github.com/answerdev/answer/pkg/converter"
|
||||
"github.com/answerdev/answer/pkg/obj"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
// RevisionService user service
|
||||
type RevisionService struct {
|
||||
revisionRepo revision.RevisionRepo
|
||||
userCommon *usercommon.UserCommon
|
||||
questionCommon *questioncommon.QuestionCommon
|
||||
answerService *AnswerService
|
||||
revisionRepo revision.RevisionRepo
|
||||
userCommon *usercommon.UserCommon
|
||||
questionCommon *questioncommon.QuestionCommon
|
||||
answerService *AnswerService
|
||||
objectInfoService *object_info.ObjService
|
||||
questionRepo questioncommon.QuestionRepo
|
||||
answerRepo answercommon.AnswerRepo
|
||||
tagRepo tag_common.TagRepo
|
||||
tagCommon *tagcommon.TagCommonService
|
||||
}
|
||||
|
||||
func NewRevisionService(
|
||||
revisionRepo revision.RevisionRepo,
|
||||
userCommon *usercommon.UserCommon,
|
||||
questionCommon *questioncommon.QuestionCommon,
|
||||
answerService *AnswerService) *RevisionService {
|
||||
answerService *AnswerService,
|
||||
objectInfoService *object_info.ObjService,
|
||||
questionRepo questioncommon.QuestionRepo,
|
||||
answerRepo answercommon.AnswerRepo,
|
||||
tagRepo tag_common.TagRepo,
|
||||
tagCommon *tagcommon.TagCommonService,
|
||||
) *RevisionService {
|
||||
return &RevisionService{
|
||||
revisionRepo: revisionRepo,
|
||||
userCommon: userCommon,
|
||||
questionCommon: questionCommon,
|
||||
answerService: answerService,
|
||||
revisionRepo: revisionRepo,
|
||||
userCommon: userCommon,
|
||||
questionCommon: questionCommon,
|
||||
answerService: answerService,
|
||||
objectInfoService: objectInfoService,
|
||||
questionRepo: questionRepo,
|
||||
answerRepo: answerRepo,
|
||||
tagRepo: tagRepo,
|
||||
tagCommon: tagCommon,
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *RevisionService) RevisionAudit(ctx context.Context, req *schema.RevisionAuditReq) (err error) {
|
||||
revisioninfo, exist, err := rs.revisionRepo.GetRevisionByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !exist {
|
||||
return
|
||||
}
|
||||
if revisioninfo.Status != entity.RevisionUnreviewedStatus {
|
||||
return
|
||||
}
|
||||
if req.Operation == schema.RevisionAuditReject {
|
||||
err = rs.revisionRepo.UpdateStatus(ctx, req.ID, entity.RevisionReviewRejectStatus)
|
||||
return
|
||||
}
|
||||
if req.Operation == schema.RevisionAuditApprove {
|
||||
objectType, objectTypeerr := obj.GetObjectTypeStrByObjectID(revisioninfo.ObjectID)
|
||||
if objectTypeerr != nil {
|
||||
return objectTypeerr
|
||||
}
|
||||
revisionitem := &schema.GetRevisionResp{}
|
||||
_ = copier.Copy(revisionitem, revisioninfo)
|
||||
rs.parseItem(ctx, revisionitem)
|
||||
var saveErr error
|
||||
switch objectType {
|
||||
case constant.QuestionObjectType:
|
||||
if !req.CanReviewQuestion {
|
||||
saveErr = errors.BadRequest(reason.RevisionNoPermission)
|
||||
} else {
|
||||
saveErr = rs.revisionAuditQuestion(ctx, revisionitem)
|
||||
}
|
||||
case constant.AnswerObjectType:
|
||||
if !req.CanReviewAnswer {
|
||||
saveErr = errors.BadRequest(reason.RevisionNoPermission)
|
||||
} else {
|
||||
saveErr = rs.revisionAuditAnswer(ctx, revisionitem)
|
||||
}
|
||||
case constant.TagObjectType:
|
||||
if !req.CanReviewTag {
|
||||
saveErr = errors.BadRequest(reason.RevisionNoPermission)
|
||||
} else {
|
||||
saveErr = rs.revisionAuditTag(ctx, revisionitem)
|
||||
}
|
||||
}
|
||||
if saveErr != nil {
|
||||
return saveErr
|
||||
}
|
||||
err = rs.revisionRepo.UpdateStatus(ctx, req.ID, entity.RevisionReviewPassStatus)
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RevisionService) revisionAuditQuestion(ctx context.Context, revisionitem *schema.GetRevisionResp) (err error) {
|
||||
questioninfo, ok := revisionitem.ContentParsed.(*schema.QuestionInfo)
|
||||
if ok {
|
||||
now := time.Now()
|
||||
question := &entity.Question{}
|
||||
question.ID = questioninfo.ID
|
||||
question.Title = questioninfo.Title
|
||||
question.OriginalText = questioninfo.Content
|
||||
question.ParsedText = questioninfo.HTML
|
||||
question.UpdatedAt = now
|
||||
question.PostUpdateTime = now
|
||||
question.LastEditUserID = revisionitem.UserID
|
||||
saveerr := rs.questionRepo.UpdateQuestion(ctx, question, []string{"title", "original_text", "parsed_text", "updated_at", "post_update_time", "last_edit_user_id"})
|
||||
if saveerr != nil {
|
||||
return saveerr
|
||||
}
|
||||
objectTagTags := make([]*schema.TagItem, 0)
|
||||
for _, tag := range questioninfo.Tags {
|
||||
item := &schema.TagItem{}
|
||||
item.SlugName = tag.SlugName
|
||||
objectTagTags = append(objectTagTags, item)
|
||||
}
|
||||
objectTagData := schema.TagChange{}
|
||||
objectTagData.ObjectID = question.ID
|
||||
objectTagData.Tags = objectTagTags
|
||||
saveerr = rs.tagCommon.ObjectChangeTag(ctx, &objectTagData)
|
||||
if saveerr != nil {
|
||||
return saveerr
|
||||
}
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: revisionitem.UserID,
|
||||
ObjectID: revisionitem.ObjectID,
|
||||
ActivityTypeKey: constant.ActQuestionEdited,
|
||||
RevisionID: revisionitem.ID,
|
||||
OriginalObjectID: revisionitem.ObjectID,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RevisionService) revisionAuditAnswer(ctx context.Context, revisionitem *schema.GetRevisionResp) (err error) {
|
||||
answerinfo, ok := revisionitem.ContentParsed.(*schema.AnswerInfo)
|
||||
if ok {
|
||||
now := time.Now()
|
||||
insertData := new(entity.Answer)
|
||||
insertData.ID = answerinfo.ID
|
||||
insertData.OriginalText = answerinfo.Content
|
||||
insertData.ParsedText = answerinfo.HTML
|
||||
insertData.UpdatedAt = now
|
||||
insertData.LastEditUserID = revisionitem.UserID
|
||||
saveerr := rs.answerRepo.UpdateAnswer(ctx, insertData, []string{"original_text", "parsed_text", "update_time", "last_edit_user_id"})
|
||||
if saveerr != nil {
|
||||
return saveerr
|
||||
}
|
||||
saveerr = rs.questionCommon.UpdataPostTime(ctx, answerinfo.QuestionID)
|
||||
if saveerr != nil {
|
||||
return saveerr
|
||||
}
|
||||
questionInfo, exist, err := rs.questionRepo.GetQuestion(ctx, answerinfo.QuestionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return errors.BadRequest(reason.QuestionNotFound)
|
||||
}
|
||||
msg := &schema.NotificationMsg{
|
||||
TriggerUserID: revisionitem.UserID,
|
||||
ReceiverUserID: questionInfo.UserID,
|
||||
Type: schema.NotificationTypeInbox,
|
||||
ObjectID: answerinfo.ID,
|
||||
}
|
||||
msg.ObjectType = constant.AnswerObjectType
|
||||
msg.NotificationAction = constant.UpdateAnswer
|
||||
notice_queue.AddNotification(msg)
|
||||
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: revisionitem.UserID,
|
||||
ObjectID: insertData.ID,
|
||||
OriginalObjectID: insertData.ID,
|
||||
ActivityTypeKey: constant.ActAnswerEdited,
|
||||
RevisionID: revisionitem.ID,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RevisionService) revisionAuditTag(ctx context.Context, revisionitem *schema.GetRevisionResp) (err error) {
|
||||
taginfo, ok := revisionitem.ContentParsed.(*schema.GetTagResp)
|
||||
if ok {
|
||||
tag := &entity.Tag{}
|
||||
tag.ID = taginfo.TagID
|
||||
tag.OriginalText = taginfo.OriginalText
|
||||
tag.ParsedText = taginfo.ParsedText
|
||||
saveerr := rs.tagRepo.UpdateTag(ctx, tag)
|
||||
if saveerr != nil {
|
||||
return saveerr
|
||||
}
|
||||
|
||||
tagInfo, exist, err := rs.tagCommon.GetTagByID(ctx, taginfo.TagID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return errors.BadRequest(reason.TagNotFound)
|
||||
}
|
||||
if tagInfo.MainTagID == 0 && len(tagInfo.SlugName) > 0 {
|
||||
log.Debugf("tag %s update slug_name", tagInfo.SlugName)
|
||||
tagList, err := rs.tagRepo.GetTagList(ctx, &entity.Tag{MainTagID: converter.StringToInt64(tagInfo.ID)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateTagSlugNames := make([]string, 0)
|
||||
for _, tag := range tagList {
|
||||
updateTagSlugNames = append(updateTagSlugNames, tag.SlugName)
|
||||
}
|
||||
err = rs.tagRepo.UpdateTagSynonym(ctx, updateTagSlugNames, converter.StringToInt64(tagInfo.ID), tagInfo.MainTagSlugName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: revisionitem.UserID,
|
||||
ObjectID: taginfo.TagID,
|
||||
OriginalObjectID: taginfo.TagID,
|
||||
ActivityTypeKey: constant.ActTagEdited,
|
||||
RevisionID: revisionitem.ID,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUnreviewedRevisionPage get unreviewed list
|
||||
func (rs *RevisionService) GetUnreviewedRevisionPage(ctx context.Context, req *schema.RevisionSearch) (
|
||||
resp *pager.PageModel, err error) {
|
||||
revisionResp := make([]*schema.GetUnreviewedRevisionResp, 0)
|
||||
if len(req.GetCanReviewObjectTypes()) == 0 {
|
||||
return pager.NewPageModel(0, revisionResp), nil
|
||||
}
|
||||
revisionPage, total, err := rs.revisionRepo.GetUnreviewedRevisionPage(
|
||||
ctx, req.Page, 1, req.GetCanReviewObjectTypes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rev := range revisionPage {
|
||||
item := &schema.GetUnreviewedRevisionResp{}
|
||||
_, ok := constant.ObjectTypeNumberMapping[rev.ObjectType]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
item.Type = constant.ObjectTypeNumberMapping[rev.ObjectType]
|
||||
info, err := rs.objectInfoService.GetUnreviewedRevisionInfo(ctx, rev.ObjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.Info = info
|
||||
revisionitem := &schema.GetRevisionResp{}
|
||||
_ = copier.Copy(revisionitem, rev)
|
||||
rs.parseItem(ctx, revisionitem)
|
||||
item.UnreviewedInfo = revisionitem
|
||||
|
||||
// get user info
|
||||
userInfo, exists, e := rs.userCommon.GetUserBasicInfoByID(ctx, revisionitem.UserID)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
if exists {
|
||||
var uinfo schema.UserBasicInfo
|
||||
err = copier.Copy(&uinfo, userInfo)
|
||||
item.UnreviewedInfo.UserInfo = uinfo
|
||||
}
|
||||
revisionResp = append(revisionResp, item)
|
||||
}
|
||||
return pager.NewPageModel(total, revisionResp), nil
|
||||
}
|
||||
|
||||
// GetRevisionList get revision list all
|
||||
func (rs *RevisionService) GetRevisionList(ctx context.Context, req *schema.GetRevisionListReq) (resp []schema.GetRevisionResp, err error) {
|
||||
var (
|
||||
|
@ -75,7 +334,7 @@ func (rs *RevisionService) GetRevisionList(ctx context.Context, req *schema.GetR
|
|||
func (rs *RevisionService) parseItem(ctx context.Context, item *schema.GetRevisionResp) {
|
||||
var (
|
||||
err error
|
||||
question entity.Question
|
||||
question entity.QuestionWithTagsRevision
|
||||
questionInfo *schema.QuestionInfo
|
||||
answer entity.Answer
|
||||
answerInfo *schema.AnswerInfo
|
||||
|
@ -89,7 +348,7 @@ func (rs *RevisionService) parseItem(ctx context.Context, item *schema.GetRevisi
|
|||
if err != nil {
|
||||
break
|
||||
}
|
||||
questionInfo = rs.questionCommon.ShowFormat(ctx, &question)
|
||||
questionInfo = rs.questionCommon.ShowFormatWithTag(ctx, &question)
|
||||
item.ContentParsed = questionInfo
|
||||
case constant.ObjectTypeStrMapping["answer"]:
|
||||
err = json.Unmarshal([]byte(item.Content), &answer)
|
||||
|
@ -125,3 +384,15 @@ func (rs *RevisionService) parseItem(ctx context.Context, item *schema.GetRevisi
|
|||
}
|
||||
item.CreatedAtParsed = item.CreatedAt.Unix()
|
||||
}
|
||||
|
||||
// CheckCanUpdateRevision can check revision
|
||||
func (rs *RevisionService) CheckCanUpdateRevision(ctx context.Context, req *schema.CheckCanQuestionUpdate) (err error) {
|
||||
_, exist, err := rs.revisionRepo.ExistUnreviewedByObjectID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exist {
|
||||
return errors.BadRequest(reason.RevisionReviewUnderway)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/search_common"
|
||||
"github.com/answerdev/answer/internal/service/tag_common"
|
||||
)
|
||||
|
||||
type TagSearch struct {
|
||||
repo search_common.SearchRepo
|
||||
tagCommonService *tag_common.TagCommonService
|
||||
followCommon activity_common.FollowRepo
|
||||
page int
|
||||
size int
|
||||
exp string
|
||||
w string
|
||||
userID string
|
||||
Extra schema.GetTagPageResp
|
||||
order string
|
||||
}
|
||||
|
||||
func NewTagSearch(repo search_common.SearchRepo,
|
||||
tagCommonService *tag_common.TagCommonService, followCommon activity_common.FollowRepo) *TagSearch {
|
||||
return &TagSearch{
|
||||
repo: repo,
|
||||
tagCommonService: tagCommonService,
|
||||
followCommon: followCommon,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse
|
||||
// example: "[tag]hello" -> {exp="tag" w="hello"}
|
||||
func (ts *TagSearch) Parse(dto *schema.SearchDTO) (ok bool) {
|
||||
exp := ""
|
||||
w := dto.Query
|
||||
q := w
|
||||
p := `(?m)^\[([a-zA-Z0-9-\+\.#]+)\]`
|
||||
|
||||
re := regexp.MustCompile(p)
|
||||
res := re.FindStringSubmatch(q)
|
||||
if len(res) == 2 {
|
||||
exp = res[1]
|
||||
trimLen := len(res[0])
|
||||
w = q[trimLen:]
|
||||
ok = true
|
||||
}
|
||||
w = strings.TrimSpace(w)
|
||||
ts.exp = exp
|
||||
ts.w = w
|
||||
ts.page = dto.Page
|
||||
ts.size = dto.Size
|
||||
ts.userID = dto.UserID
|
||||
ts.order = dto.Order
|
||||
return ok
|
||||
}
|
||||
|
||||
func (ts *TagSearch) Search(ctx context.Context) (resp []schema.SearchResp, total int64, err error) {
|
||||
var (
|
||||
words []string
|
||||
tag *entity.Tag
|
||||
exists, followed bool
|
||||
)
|
||||
tag, exists, err = ts.tagCommonService.GetTagBySlugName(ctx, ts.exp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if ts.userID != "" {
|
||||
followed, err = ts.followCommon.IsFollowed(ts.userID, tag.ID)
|
||||
}
|
||||
|
||||
ts.Extra = schema.GetTagPageResp{
|
||||
TagID: tag.ID,
|
||||
SlugName: tag.SlugName,
|
||||
DisplayName: tag.DisplayName,
|
||||
OriginalText: tag.OriginalText,
|
||||
ParsedText: tag.ParsedText,
|
||||
QuestionCount: tag.QuestionCount,
|
||||
IsFollower: followed,
|
||||
Recommend: tag.Recommend,
|
||||
Reserved: tag.Reserved,
|
||||
}
|
||||
ts.Extra.GetExcerpt()
|
||||
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
words = strings.Split(ts.w, " ")
|
||||
if len(words) > 3 {
|
||||
words = words[:4]
|
||||
}
|
||||
|
||||
resp, total, err = ts.repo.SearchContents(ctx, words, tag.ID, "", -1, ts.page, ts.size, ts.order)
|
||||
|
||||
return
|
||||
}
|
|
@ -4,9 +4,11 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
"github.com/answerdev/answer/internal/service/siteinfo_common"
|
||||
"github.com/answerdev/answer/internal/service/tag_common"
|
||||
tagcommonser "github.com/answerdev/answer/internal/service/tag_common"
|
||||
"github.com/answerdev/answer/pkg/htmltext"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/pager"
|
||||
|
@ -21,17 +23,10 @@ import (
|
|||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
type TagRepo interface {
|
||||
RemoveTag(ctx context.Context, tagID string) (err error)
|
||||
UpdateTag(ctx context.Context, tag *entity.Tag) (err error)
|
||||
UpdateTagSynonym(ctx context.Context, tagSlugNameList []string, mainTagID int64, mainTagSlugName string) (err error)
|
||||
GetTagList(ctx context.Context, tag *entity.Tag) (tagList []*entity.Tag, err error)
|
||||
}
|
||||
|
||||
// TagService user service
|
||||
type TagService struct {
|
||||
tagRepo TagRepo
|
||||
tagCommonService *tag_common.TagCommonService
|
||||
tagRepo tagcommonser.TagRepo
|
||||
tagCommonService *tagcommonser.TagCommonService
|
||||
revisionService *revision_common.RevisionService
|
||||
followCommon activity_common.FollowRepo
|
||||
siteInfoService *siteinfo_common.SiteInfoCommonService
|
||||
|
@ -39,8 +34,8 @@ type TagService struct {
|
|||
|
||||
// NewTagService new tag service
|
||||
func NewTagService(
|
||||
tagRepo TagRepo,
|
||||
tagCommonService *tag_common.TagCommonService,
|
||||
tagRepo tagcommonser.TagRepo,
|
||||
tagCommonService *tagcommonser.TagCommonService,
|
||||
revisionService *revision_common.RevisionService,
|
||||
followCommon activity_common.FollowRepo,
|
||||
siteInfoService *siteinfo_common.SiteInfoCommonService) *TagService {
|
||||
|
@ -66,50 +61,7 @@ func (ts *TagService) RemoveTag(ctx context.Context, tagID string) (err error) {
|
|||
|
||||
// UpdateTag update tag
|
||||
func (ts *TagService) UpdateTag(ctx context.Context, req *schema.UpdateTagReq) (err error) {
|
||||
tag := &entity.Tag{}
|
||||
_ = copier.Copy(tag, req)
|
||||
tag.ID = req.TagID
|
||||
err = ts.tagRepo.UpdateTag(ctx, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tagInfo, exist, err := ts.tagCommonService.GetTagByID(ctx, req.TagID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return errors.BadRequest(reason.TagNotFound)
|
||||
}
|
||||
if tagInfo.MainTagID == 0 && len(req.SlugName) > 0 {
|
||||
log.Debugf("tag %s update slug_name", tagInfo.SlugName)
|
||||
tagList, err := ts.tagRepo.GetTagList(ctx, &entity.Tag{MainTagID: converter.StringToInt64(tagInfo.ID)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateTagSlugNames := make([]string, 0)
|
||||
for _, tag := range tagList {
|
||||
updateTagSlugNames = append(updateTagSlugNames, tag.SlugName)
|
||||
}
|
||||
err = ts.tagRepo.UpdateTagSynonym(ctx, updateTagSlugNames, converter.StringToInt64(tagInfo.ID), tagInfo.MainTagSlugName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
revisionDTO := &schema.AddRevisionDTO{
|
||||
UserID: req.UserID,
|
||||
ObjectID: tag.ID,
|
||||
Title: tag.SlugName,
|
||||
Log: req.EditSummary,
|
||||
}
|
||||
tagInfoJson, _ := json.Marshal(tagInfo)
|
||||
revisionDTO.Content = string(tagInfoJson)
|
||||
err = ts.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
return ts.tagCommonService.UpdateTag(ctx, req)
|
||||
}
|
||||
|
||||
// GetTagInfo get tag one
|
||||
|
@ -154,7 +106,7 @@ func (ts *TagService) GetTagInfo(ctx context.Context, req *schema.GetTagInfoReq)
|
|||
resp.Recommend = tagInfo.Recommend
|
||||
resp.Reserved = tagInfo.Reserved
|
||||
resp.IsFollower = ts.checkTagIsFollow(ctx, req.UserID, tagInfo.ID)
|
||||
resp.MemberActions = permission.GetTagPermission(req.UserID, req.UserID)
|
||||
resp.MemberActions = permission.GetTagPermission(ctx, req.CanEdit, req.CanDelete)
|
||||
resp.GetExcerpt()
|
||||
return resp, nil
|
||||
}
|
||||
|
@ -198,7 +150,8 @@ func (ts *TagService) GetFollowingTags(ctx context.Context, userID string) (
|
|||
|
||||
// GetTagSynonyms get tag synonyms
|
||||
func (ts *TagService) GetTagSynonyms(ctx context.Context, req *schema.GetTagSynonymsReq) (
|
||||
resp []*schema.GetTagSynonymsResp, err error) {
|
||||
resp *schema.GetTagSynonymsResp, err error) {
|
||||
resp = &schema.GetTagSynonymsResp{Synonyms: make([]*schema.TagSynonym, 0)}
|
||||
tag, exist, err := ts.tagCommonService.GetTagByID(ctx, req.TagID)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -230,15 +183,15 @@ func (ts *TagService) GetTagSynonyms(ctx context.Context, req *schema.GetTagSyno
|
|||
mainTagSlugName = tag.SlugName
|
||||
}
|
||||
|
||||
resp = make([]*schema.GetTagSynonymsResp, 0)
|
||||
for _, t := range tagList {
|
||||
resp = append(resp, &schema.GetTagSynonymsResp{
|
||||
resp.Synonyms = append(resp.Synonyms, &schema.TagSynonym{
|
||||
TagID: t.ID,
|
||||
SlugName: t.SlugName,
|
||||
DisplayName: t.DisplayName,
|
||||
MainTagSlugName: mainTagSlugName,
|
||||
})
|
||||
}
|
||||
resp.MemberActions = permission.GetTagSynonymPermission(ctx, req.CanEdit)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -281,6 +234,7 @@ func (ts *TagService) UpdateTagSynonym(ctx context.Context, req *schema.UpdateTa
|
|||
item.OriginalText = tag.OriginalText
|
||||
item.ParsedText = tag.ParsedText
|
||||
item.Status = entity.TagStatusAvailable
|
||||
item.UserID = req.UserID
|
||||
needAddTagList = append(needAddTagList, item)
|
||||
}
|
||||
|
||||
|
@ -299,10 +253,17 @@ func (ts *TagService) UpdateTagSynonym(ctx context.Context, req *schema.UpdateTa
|
|||
}
|
||||
tagInfoJson, _ := json.Marshal(tag)
|
||||
revisionDTO.Content = string(tagInfoJson)
|
||||
err = ts.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
revisionID, err := ts.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: req.UserID,
|
||||
ObjectID: tag.ID,
|
||||
OriginalObjectID: tag.ID,
|
||||
ActivityTypeKey: constant.ActTagCreated,
|
||||
RevisionID: revisionID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,6 +300,7 @@ func (ts *TagService) UpdateTagSynonym(ctx context.Context, req *schema.UpdateTa
|
|||
func (ts *TagService) GetTagWithPage(ctx context.Context, req *schema.GetTagWithPageReq) (pageModel *pager.PageModel, err error) {
|
||||
tag := &entity.Tag{}
|
||||
_ = copier.Copy(tag, req)
|
||||
tag.UserID = ""
|
||||
|
||||
page := req.Page
|
||||
pageSize := req.PageSize
|
||||
|
|
|
@ -7,12 +7,15 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/base/validator"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
"github.com/answerdev/answer/internal/service/siteinfo_common"
|
||||
"github.com/answerdev/answer/pkg/converter"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
@ -31,6 +34,13 @@ type TagCommonRepo interface {
|
|||
UpdateTagQuestionCount(ctx context.Context, tagID string, questionCount int) (err error)
|
||||
}
|
||||
|
||||
type TagRepo interface {
|
||||
RemoveTag(ctx context.Context, tagID string) (err error)
|
||||
UpdateTag(ctx context.Context, tag *entity.Tag) (err error)
|
||||
UpdateTagSynonym(ctx context.Context, tagSlugNameList []string, mainTagID int64, mainTagSlugName string) (err error)
|
||||
GetTagList(ctx context.Context, tag *entity.Tag) (tagList []*entity.Tag, err error)
|
||||
}
|
||||
|
||||
type TagRelRepo interface {
|
||||
AddTagRelList(ctx context.Context, tagList []*entity.TagRel) (err error)
|
||||
RemoveTagRelListByIDs(ctx context.Context, ids []int64) (err error)
|
||||
|
@ -46,17 +56,22 @@ type TagCommonService struct {
|
|||
revisionService *revision_common.RevisionService
|
||||
tagCommonRepo TagCommonRepo
|
||||
tagRelRepo TagRelRepo
|
||||
tagRepo TagRepo
|
||||
siteInfoService *siteinfo_common.SiteInfoCommonService
|
||||
}
|
||||
|
||||
// NewTagCommonService new tag service
|
||||
func NewTagCommonService(tagCommonRepo TagCommonRepo, tagRelRepo TagRelRepo,
|
||||
func NewTagCommonService(
|
||||
tagCommonRepo TagCommonRepo,
|
||||
tagRelRepo TagRelRepo,
|
||||
tagRepo TagRepo,
|
||||
revisionService *revision_common.RevisionService,
|
||||
siteInfoService *siteinfo_common.SiteInfoCommonService,
|
||||
) *TagCommonService {
|
||||
return &TagCommonService{
|
||||
tagCommonRepo: tagCommonRepo,
|
||||
tagRelRepo: tagRelRepo,
|
||||
tagRepo: tagRepo,
|
||||
revisionService: revisionService,
|
||||
siteInfoService: siteInfoService,
|
||||
}
|
||||
|
@ -68,7 +83,7 @@ func (ts *TagCommonService) SearchTagLike(ctx context.Context, req *schema.Searc
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
ts.tagsFormatRecommendAndReserved(ctx, tags)
|
||||
ts.TagsFormatRecommendAndReserved(ctx, tags)
|
||||
for _, tag := range tags {
|
||||
item := schema.SearchTagLikeResp{}
|
||||
item.SlugName = tag.SlugName
|
||||
|
@ -166,7 +181,7 @@ func (ts *TagCommonService) GetTagListByNames(ctx context.Context, tagNames []st
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts.tagsFormatRecommendAndReserved(ctx, tagList)
|
||||
ts.TagsFormatRecommendAndReserved(ctx, tagList)
|
||||
return tagList, nil
|
||||
}
|
||||
|
||||
|
@ -231,7 +246,7 @@ func (ts *TagCommonService) GetTagListByIDs(ctx context.Context, ids []string) (
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts.tagsFormatRecommendAndReserved(ctx, tagList)
|
||||
ts.TagsFormatRecommendAndReserved(ctx, tagList)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -242,7 +257,7 @@ func (ts *TagCommonService) GetTagPage(ctx context.Context, page, pageSize int,
|
|||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ts.tagsFormatRecommendAndReserved(ctx, tagList)
|
||||
ts.TagsFormatRecommendAndReserved(ctx, tagList)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -276,7 +291,7 @@ func (ts *TagCommonService) TagFormat(ctx context.Context, tags []*entity.Tag) (
|
|||
return objTags, nil
|
||||
}
|
||||
|
||||
func (ts *TagCommonService) tagsFormatRecommendAndReserved(ctx context.Context, tagList []*entity.Tag) {
|
||||
func (ts *TagCommonService) TagsFormatRecommendAndReserved(ctx context.Context, tagList []*entity.Tag) {
|
||||
if len(tagList) == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -389,6 +404,7 @@ func (ts *TagCommonService) CheckTag(ctx context.Context, tags []string, userID
|
|||
item.OriginalText = ""
|
||||
item.ParsedText = ""
|
||||
item.Status = entity.TagStatusAvailable
|
||||
item.UserID = userID
|
||||
addTagList = append(addTagList, item)
|
||||
addTagMsgList = append(addTagMsgList, tag)
|
||||
}
|
||||
|
@ -403,7 +419,31 @@ func (ts *TagCommonService) CheckTag(ctx context.Context, tags []string, userID
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ts *TagCommonService) ObjectCheckChangeTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, []string) {
|
||||
// CheckTagsIsChange
|
||||
func (ts *TagCommonService) CheckTagsIsChange(ctx context.Context, tagNameList, oldtagNameList []string) bool {
|
||||
check := make(map[string]bool)
|
||||
if len(tagNameList) != len(oldtagNameList) {
|
||||
return true
|
||||
}
|
||||
for _, item := range tagNameList {
|
||||
check[item] = false
|
||||
}
|
||||
for _, item := range oldtagNameList {
|
||||
_, ok := check[item]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
check[item] = true
|
||||
}
|
||||
for _, value := range check {
|
||||
if value == false {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ts *TagCommonService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, []string) {
|
||||
reservedTagsMap := make(map[string]bool)
|
||||
needTagsMap := make([]string, 0)
|
||||
for _, tag := range objectTagData {
|
||||
|
@ -463,6 +503,7 @@ func (ts *TagCommonService) ObjectChangeTag(ctx context.Context, objectTagData *
|
|||
item.OriginalText = tag.OriginalText
|
||||
item.ParsedText = tag.ParsedText
|
||||
item.Status = entity.TagStatusAvailable
|
||||
item.UserID = objectTagData.UserID
|
||||
addTagList = append(addTagList, item)
|
||||
}
|
||||
|
||||
|
@ -480,10 +521,17 @@ func (ts *TagCommonService) ObjectChangeTag(ctx context.Context, objectTagData *
|
|||
}
|
||||
tagInfoJson, _ := json.Marshal(tag)
|
||||
revisionDTO.Content = string(tagInfoJson)
|
||||
err = ts.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
revisionID, err := ts.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: objectTagData.UserID,
|
||||
ObjectID: tag.ID,
|
||||
OriginalObjectID: tag.ID,
|
||||
ActivityTypeKey: constant.ActTagCreated,
|
||||
RevisionID: revisionID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,3 +621,83 @@ func (ts *TagCommonService) CreateOrUpdateTagRelList(ctx context.Context, object
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ts *TagCommonService) UpdateTag(ctx context.Context, req *schema.UpdateTagReq) (err error) {
|
||||
var canUpdate bool
|
||||
_, existUnreviewed, err := ts.revisionService.ExistUnreviewedByObjectID(ctx, req.TagID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existUnreviewed {
|
||||
err = errors.BadRequest(reason.AnswerCannotUpdate)
|
||||
return err
|
||||
}
|
||||
|
||||
tagInfo, exist, err := ts.GetTagByID(ctx, req.TagID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return errors.BadRequest(reason.TagNotFound)
|
||||
}
|
||||
//If the content is the same, ignore it
|
||||
if tagInfo.OriginalText == req.OriginalText {
|
||||
return nil
|
||||
}
|
||||
|
||||
tagInfo.SlugName = req.SlugName
|
||||
tagInfo.DisplayName = req.DisplayName
|
||||
tagInfo.OriginalText = req.OriginalText
|
||||
tagInfo.ParsedText = req.ParsedText
|
||||
|
||||
revisionDTO := &schema.AddRevisionDTO{
|
||||
UserID: req.UserID,
|
||||
ObjectID: tagInfo.ID,
|
||||
Title: tagInfo.SlugName,
|
||||
Log: req.EditSummary,
|
||||
}
|
||||
|
||||
if req.NoNeedReview {
|
||||
canUpdate = true
|
||||
err = ts.tagRepo.UpdateTag(ctx, tagInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tagInfo.MainTagID == 0 && len(req.SlugName) > 0 {
|
||||
log.Debugf("tag %s update slug_name", tagInfo.SlugName)
|
||||
tagList, err := ts.tagRepo.GetTagList(ctx, &entity.Tag{MainTagID: converter.StringToInt64(tagInfo.ID)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateTagSlugNames := make([]string, 0)
|
||||
for _, tag := range tagList {
|
||||
updateTagSlugNames = append(updateTagSlugNames, tag.SlugName)
|
||||
}
|
||||
err = ts.tagRepo.UpdateTagSynonym(ctx, updateTagSlugNames, converter.StringToInt64(tagInfo.ID), tagInfo.MainTagSlugName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
revisionDTO.Status = entity.RevisionReviewPassStatus
|
||||
} else {
|
||||
revisionDTO.Status = entity.RevisionUnreviewedStatus
|
||||
}
|
||||
|
||||
tagInfoJson, _ := json.Marshal(tagInfo)
|
||||
revisionDTO.Content = string(tagInfoJson)
|
||||
revisionID, err := ts.revisionService.AddRevision(ctx, revisionDTO, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if canUpdate {
|
||||
activity_queue.AddActivity(&schema.ActivityMsg{
|
||||
UserID: req.UserID,
|
||||
ObjectID: tagInfo.ID,
|
||||
OriginalObjectID: tagInfo.ID,
|
||||
ActivityTypeKey: constant.ActTagEdited,
|
||||
RevisionID: revisionID,
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -38,12 +38,13 @@ func NewUserCommon(userRepo UserRepo) *UserCommon {
|
|||
}
|
||||
}
|
||||
|
||||
func (us *UserCommon) GetUserBasicInfoByID(ctx context.Context, ID string) (*schema.UserBasicInfo, bool, error) {
|
||||
func (us *UserCommon) GetUserBasicInfoByID(ctx context.Context, ID string) (
|
||||
userBasicInfo *schema.UserBasicInfo, exist bool, err error) {
|
||||
userInfo, exist, err := us.userRepo.GetByUserID(ctx, ID)
|
||||
if err != nil {
|
||||
return nil, exist, err
|
||||
}
|
||||
info := us.UserBasicInfoFormat(ctx, userInfo)
|
||||
info := us.FormatUserBasicInfo(ctx, userInfo)
|
||||
return info, exist, nil
|
||||
}
|
||||
|
||||
|
@ -52,7 +53,7 @@ func (us *UserCommon) GetUserBasicInfoByUserName(ctx context.Context, username s
|
|||
if err != nil {
|
||||
return nil, exist, err
|
||||
}
|
||||
info := us.UserBasicInfoFormat(ctx, userInfo)
|
||||
info := us.FormatUserBasicInfo(ctx, userInfo)
|
||||
return info, exist, nil
|
||||
}
|
||||
|
||||
|
@ -71,21 +72,21 @@ func (us *UserCommon) BatchUserBasicInfoByID(ctx context.Context, IDs []string)
|
|||
return userMap, err
|
||||
}
|
||||
for _, item := range dbInfo {
|
||||
info := us.UserBasicInfoFormat(ctx, item)
|
||||
info := us.FormatUserBasicInfo(ctx, item)
|
||||
userMap[item.ID] = info
|
||||
}
|
||||
return userMap, nil
|
||||
}
|
||||
|
||||
// UserBasicInfoFormat
|
||||
func (us *UserCommon) UserBasicInfoFormat(ctx context.Context, userInfo *entity.User) *schema.UserBasicInfo {
|
||||
// FormatUserBasicInfo format user basic info
|
||||
func (us *UserCommon) FormatUserBasicInfo(ctx context.Context, userInfo *entity.User) *schema.UserBasicInfo {
|
||||
userBasicInfo := &schema.UserBasicInfo{}
|
||||
Avatar := schema.FormatAvatarInfo(userInfo.Avatar)
|
||||
userBasicInfo.ID = userInfo.ID
|
||||
userBasicInfo.IsAdmin = userInfo.IsAdmin
|
||||
userBasicInfo.Username = userInfo.Username
|
||||
userBasicInfo.Rank = userInfo.Rank
|
||||
userBasicInfo.DisplayName = userInfo.DisplayName
|
||||
userBasicInfo.Avatar = Avatar
|
||||
userBasicInfo.Avatar = schema.FormatAvatarInfo(userInfo.Avatar)
|
||||
userBasicInfo.Website = userInfo.Website
|
||||
userBasicInfo.Location = userInfo.Location
|
||||
userBasicInfo.IPInfo = userInfo.IPInfo
|
||||
|
|
|
@ -160,20 +160,10 @@ const routes: RouteNode[] = [
|
|||
{
|
||||
path: 'users/password-reset',
|
||||
page: 'pages/Users/PasswordReset',
|
||||
guard: async () => {
|
||||
return guard.activated();
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'users/account-activation',
|
||||
page: 'pages/Users/ActiveEmail',
|
||||
// guard: async () => {
|
||||
// const notActivated = guard.notActivated();
|
||||
// if (notActivated.ok) {
|
||||
// return notActivated;
|
||||
// }
|
||||
// return guard.notLogged();
|
||||
// },
|
||||
},
|
||||
{
|
||||
path: 'users/account-activation/success',
|
||||
|
|
Loading…
Reference in New Issue