From bbf7849fb7f63a9ac36ceafe3c6b1a2ed856152b Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Mon, 22 May 2023 18:42:28 +0800 Subject: [PATCH 01/14] update invite --- internal/entity/question_entity.go | 1 + internal/migrations/v13.go | 35 ++++++++++++++++++++++++++++ internal/repo/user/user_repo.go | 11 +++++++++ internal/schema/question_schema.go | 6 +++-- internal/service/question_service.go | 8 +++++++ internal/service/user_common/user.go | 14 +++++++++++ 6 files changed, 73 insertions(+), 2 deletions(-) diff --git a/internal/entity/question_entity.go b/internal/entity/question_entity.go index 41c9236b..bfcc24ef 100644 --- a/internal/entity/question_entity.go +++ b/internal/entity/question_entity.go @@ -32,6 +32,7 @@ type Question struct { CreatedAt time.Time `xorm:"not null default CURRENT_TIMESTAMP TIMESTAMP created_at"` UpdatedAt time.Time `xorm:"updated_at TIMESTAMP"` UserID string `xorm:"not null default 0 BIGINT(20) INDEX user_id"` + InviteUserID string `xorm:"TEXT invite_user_id"` LastEditUserID string `xorm:"not null default 0 BIGINT(20) last_edit_user_id"` Title string `xorm:"not null default '' VARCHAR(150) title"` OriginalText string `xorm:"not null MEDIUMTEXT original_text"` diff --git a/internal/migrations/v13.go b/internal/migrations/v13.go index f86f38f8..74ae8aea 100644 --- a/internal/migrations/v13.go +++ b/internal/migrations/v13.go @@ -2,6 +2,7 @@ package migrations import ( "fmt" + "time" "github.com/answerdev/answer/internal/entity" "github.com/segmentfault/pacman/log" @@ -13,6 +14,7 @@ func updateCount(x *xorm.Engine) error { updateTagCount(x) updateUserQuestionCount(x) updateUserAnswerCount(x) + inviteAnswer(x) return nil } @@ -215,3 +217,36 @@ func updateUserAnswerCount(x *xorm.Engine) error { } return nil } + +func inviteAnswer(x *xorm.Engine) error { + type Question struct { + ID string `xorm:"not null pk BIGINT(20) id"` + CreatedAt time.Time `xorm:"not null default CURRENT_TIMESTAMP TIMESTAMP created_at"` + UpdatedAt time.Time `xorm:"updated_at TIMESTAMP"` + UserID string `xorm:"not null default 0 BIGINT(20) INDEX user_id"` + InviteUserID string `xorm:"TEXT invite_user_id"` + LastEditUserID string `xorm:"not null default 0 BIGINT(20) last_edit_user_id"` + Title string `xorm:"not null default '' VARCHAR(150) title"` + OriginalText string `xorm:"not null MEDIUMTEXT original_text"` + ParsedText string `xorm:"not null MEDIUMTEXT parsed_text"` + Status int `xorm:"not null default 1 INT(11) status"` + Pin int `xorm:"not null default 1 INT(11) pin"` + Show int `xorm:"not null default 1 INT(11) show"` + ViewCount int `xorm:"not null default 0 INT(11) view_count"` + UniqueViewCount int `xorm:"not null default 0 INT(11) unique_view_count"` + VoteCount int `xorm:"not null default 0 INT(11) vote_count"` + AnswerCount int `xorm:"not null default 0 INT(11) answer_count"` + CollectionCount int `xorm:"not null default 0 INT(11) collection_count"` + FollowCount int `xorm:"not null default 0 INT(11) follow_count"` + AcceptedAnswerID string `xorm:"not null default 0 BIGINT(20) accepted_answer_id"` + LastAnswerID string `xorm:"not null default 0 BIGINT(20) last_answer_id"` + PostUpdateTime time.Time `xorm:"post_update_time TIMESTAMP"` + RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"` + } + err := x.Sync(new(Question)) + if err != nil { + return err + } + + return nil +} diff --git a/internal/repo/user/user_repo.go b/internal/repo/user/user_repo.go index 53c46ca0..b26bcaa1 100644 --- a/internal/repo/user/user_repo.go +++ b/internal/repo/user/user_repo.go @@ -195,6 +195,17 @@ func (ur *userRepo) GetByUsername(ctx context.Context, username string) (userInf return } +func (ur *userRepo) GetByUsernames(ctx context.Context, usernames []string) ([]*entity.User, error) { + list := make([]*entity.User, 0) + err := ur.data.DB.Where("status =?", entity.UserStatusAvailable).In("username", usernames).Find(&list) + if err != nil { + err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + return list, err + } + tryToDecorateUserListFromUserCenter(ctx, ur.data, list) + return list, nil +} + // GetByEmail get user by email func (ur *userRepo) GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error) { userInfo = &entity.User{} diff --git a/internal/schema/question_schema.go b/internal/schema/question_schema.go index 60af4f14..9a0f4073 100644 --- a/internal/schema/question_schema.go +++ b/internal/schema/question_schema.go @@ -61,7 +61,8 @@ type QuestionAdd struct { // tags Tags []*TagItem `validate:"required,dive" json:"tags"` // user id - UserID string `json:"-"` + UserID string `json:"-"` + InviteUser []string `validate:"omitempty" json:"invite_user"` QuestionPermission } @@ -87,7 +88,8 @@ type QuestionAddByAnswer struct { // tags Tags []*TagItem `validate:"required,dive" json:"tags"` // user id - UserID string `json:"-"` + UserID string `json:"-"` + MentionUsernameList []string `validate:"omitempty" json:"mention_username_list"` QuestionPermission } diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 25820563..7c141123 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -28,6 +28,7 @@ import ( usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/answerdev/answer/pkg/htmltext" "github.com/answerdev/answer/pkg/uid" + "github.com/davecgh/go-spew/spew" "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/i18n" @@ -258,6 +259,13 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question } } + //verify invite user + inviteUserInfoList, err := qs.userCommon.BatchGetUserBasicInfoByUserNames(ctx, req.InviteUser) + if err != nil { + log.Error("BatchGetUserBasicInfoByUserNames error", err.Error()) + } + spew.Dump(inviteUserInfoList) + question := &entity.Question{} now := time.Now() question.UserID = req.UserID diff --git a/internal/service/user_common/user.go b/internal/service/user_common/user.go index c5786746..d7845f22 100644 --- a/internal/service/user_common/user.go +++ b/internal/service/user_common/user.go @@ -32,6 +32,7 @@ type UserRepo interface { GetByUserID(ctx context.Context, userID string) (userInfo *entity.User, exist bool, err error) BatchGetByID(ctx context.Context, ids []string) ([]*entity.User, error) GetByUsername(ctx context.Context, username string) (userInfo *entity.User, exist bool, err error) + GetByUsernames(ctx context.Context, usernames []string) ([]*entity.User, error) GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error) GetUserCount(ctx context.Context) (count int64, err error) } @@ -74,6 +75,19 @@ func (us *UserCommon) GetUserBasicInfoByUserName(ctx context.Context, username s return info, exist, nil } +func (us *UserCommon) BatchGetUserBasicInfoByUserNames(ctx context.Context, usernames []string) ([]*schema.UserBasicInfo, error) { + infolist := make([]*schema.UserBasicInfo, 0) + list, err := us.userRepo.GetByUsernames(ctx, usernames) + if err != nil { + return infolist, err + } + for _, user := range list { + info := us.FormatUserBasicInfo(ctx, user) + infolist = append(infolist, info) + } + return infolist, nil +} + func (us *UserCommon) UpdateAnswerCount(ctx context.Context, userID string, num int) error { return us.userRepo.UpdateAnswerCount(ctx, userID, num) } From 80b6b786c536f4b96aed36f775eb205d85615fba Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 23 May 2023 12:15:52 +0800 Subject: [PATCH 02/14] update inviteUser --- docs/docs.go | 12 +++++++++++ docs/swagger.json | 12 +++++++++++ docs/swagger.yaml | 8 +++++++ internal/schema/question_schema.go | 6 +++--- internal/service/question_service.go | 32 ++++++++++++++++++++-------- internal/service/user_common/user.go | 10 ++++----- 6 files changed, 63 insertions(+), 17 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 3310c0b2..d2fe7190 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -7618,6 +7618,12 @@ const docTemplate = `{ "maxLength": 65535, "minLength": 6 }, + "mention_username_list": { + "type": "array", + "items": { + "type": "string" + } + }, "tags": { "description": "tags", "type": "array", @@ -7781,6 +7787,12 @@ const docTemplate = `{ "description": "question id", "type": "string" }, + "invite_user": { + "type": "array", + "items": { + "type": "string" + } + }, "tags": { "description": "tags", "type": "array", diff --git a/docs/swagger.json b/docs/swagger.json index d8bcb632..3a1afb34 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -7606,6 +7606,12 @@ "maxLength": 65535, "minLength": 6 }, + "mention_username_list": { + "type": "array", + "items": { + "type": "string" + } + }, "tags": { "description": "tags", "type": "array", @@ -7769,6 +7775,12 @@ "description": "question id", "type": "string" }, + "invite_user": { + "type": "array", + "items": { + "type": "string" + } + }, "tags": { "description": "tags", "type": "array", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index a97803db..b73f8efe 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1218,6 +1218,10 @@ definitions: maxLength: 65535 minLength: 6 type: string + mention_username_list: + items: + type: string + type: array tags: description: tags items: @@ -1334,6 +1338,10 @@ definitions: id: description: question id type: string + invite_user: + items: + type: string + type: array tags: description: tags items: diff --git a/internal/schema/question_schema.go b/internal/schema/question_schema.go index 9a0f4073..9ef45d7a 100644 --- a/internal/schema/question_schema.go +++ b/internal/schema/question_schema.go @@ -61,8 +61,7 @@ type QuestionAdd struct { // tags Tags []*TagItem `validate:"required,dive" json:"tags"` // user id - UserID string `json:"-"` - InviteUser []string `validate:"omitempty" json:"invite_user"` + UserID string `json:"-"` QuestionPermission } @@ -141,7 +140,8 @@ type QuestionUpdate struct { // content Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"` // html - HTML string `json:"-"` + HTML string `json:"-"` + InviteUser []string `validate:"omitempty" json:"invite_user"` // tags Tags []*TagItem `validate:"required,dive" json:"tags"` // edit summary diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 7c141123..73c89386 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -28,7 +28,6 @@ import ( usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/answerdev/answer/pkg/htmltext" "github.com/answerdev/answer/pkg/uid" - "github.com/davecgh/go-spew/spew" "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/i18n" @@ -259,13 +258,6 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question } } - //verify invite user - inviteUserInfoList, err := qs.userCommon.BatchGetUserBasicInfoByUserNames(ctx, req.InviteUser) - if err != nil { - log.Error("BatchGetUserBasicInfoByUserNames error", err.Error()) - } - spew.Dump(inviteUserInfoList) - question := &entity.Question{} now := time.Now() question.UserID = req.UserID @@ -585,6 +577,27 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest return nil, err } + //verify invite user + inviteUserInfoList, err := qs.userCommon.BatchGetUserBasicInfoByUserNames(ctx, req.InviteUser) + if err != nil { + log.Error("BatchGetUserBasicInfoByUserNames error", err.Error()) + } + inviteUser := make([]string, 0) + for _, item := range req.InviteUser { + _, ok := inviteUserInfoList[item] + if ok { + inviteUser = append(inviteUser, inviteUserInfoList[item].ID) + } + } + inviteUserStr := "" + inviteUserByte, err := json.Marshal(inviteUser) + if err != nil { + log.Error("json.Marshal error", err.Error()) + inviteUserStr = "[]" + } else { + inviteUserStr = string(inviteUserByte) + } + now := time.Now() question := &entity.Question{} question.Title = req.Title @@ -592,6 +605,7 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest question.ParsedText = req.HTML question.ID = uid.DeShortID(req.ID) question.UpdatedAt = now + question.InviteUserID = inviteUserStr question.PostUpdateTime = now question.UserID = dbinfo.UserID question.LastEditUserID = req.UserID @@ -685,7 +699,7 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest //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"}) + saveerr := qs.questionRepo.UpdateQuestion(ctx, question, []string{"title", "original_text", "parsed_text", "updated_at", "post_update_time", "last_edit_user_id", "invite_user_id"}) if saveerr != nil { return questionInfo, saveerr } diff --git a/internal/service/user_common/user.go b/internal/service/user_common/user.go index d7845f22..54f4bf64 100644 --- a/internal/service/user_common/user.go +++ b/internal/service/user_common/user.go @@ -75,17 +75,17 @@ func (us *UserCommon) GetUserBasicInfoByUserName(ctx context.Context, username s return info, exist, nil } -func (us *UserCommon) BatchGetUserBasicInfoByUserNames(ctx context.Context, usernames []string) ([]*schema.UserBasicInfo, error) { - infolist := make([]*schema.UserBasicInfo, 0) +func (us *UserCommon) BatchGetUserBasicInfoByUserNames(ctx context.Context, usernames []string) (map[string]*schema.UserBasicInfo, error) { + infomap := make(map[string]*schema.UserBasicInfo) list, err := us.userRepo.GetByUsernames(ctx, usernames) if err != nil { - return infolist, err + return infomap, err } for _, user := range list { info := us.FormatUserBasicInfo(ctx, user) - infolist = append(infolist, info) + infomap[user.Username] = info } - return infolist, nil + return infomap, nil } func (us *UserCommon) UpdateAnswerCount(ctx context.Context, userID string, num int) error { From 144caea4f994858233c58e9b58c21c768348b26b Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 23 May 2023 14:54:59 +0800 Subject: [PATCH 03/14] update invite user --- docs/docs.go | 49 ++++++++++++++++++++++++++ docs/swagger.json | 49 ++++++++++++++++++++++++++ docs/swagger.yaml | 28 +++++++++++++++ internal/controller/user_controller.go | 19 ++++++++++ internal/repo/user/user_repo.go | 16 +++++++++ internal/router/answer_api_router.go | 1 + internal/service/user_common/user.go | 1 + internal/service/user_service.go | 13 +++++++ 8 files changed, 176 insertions(+) diff --git a/docs/docs.go b/docs/docs.go index d2fe7190..77a19c21 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -5103,6 +5103,55 @@ const docTemplate = `{ } } }, + "/answer/api/v1/user/info/search": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "SearchUserListByName", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "User" + ], + "summary": "SearchUserListByName", + "parameters": [ + { + "type": "string", + "description": "username", + "name": "username", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.GetOtherUserInfoResp" + } + } + } + ] + } + } + } + } + }, "/answer/api/v1/user/interface": { "put": { "security": [ diff --git a/docs/swagger.json b/docs/swagger.json index 3a1afb34..31fdf372 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -5091,6 +5091,55 @@ } } }, + "/answer/api/v1/user/info/search": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "SearchUserListByName", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "User" + ], + "summary": "SearchUserListByName", + "parameters": [ + { + "type": "string", + "description": "username", + "name": "username", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.GetOtherUserInfoResp" + } + } + } + ] + } + } + } + } + }, "/answer/api/v1/user/interface": { "put": { "security": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index b73f8efe..b3ef8a9d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -5364,6 +5364,34 @@ paths: summary: UserUpdateInfo update user info tags: - User + /answer/api/v1/user/info/search: + get: + consumes: + - application/json + description: SearchUserListByName + parameters: + - description: username + in: query + name: username + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/handler.RespBody' + - properties: + data: + $ref: '#/definitions/schema.GetOtherUserInfoResp' + type: object + security: + - ApiKeyAuth: [] + summary: SearchUserListByName + tags: + - User /answer/api/v1/user/interface: put: consumes: diff --git a/internal/controller/user_controller.go b/internal/controller/user_controller.go index 5266e66b..4fbf0482 100644 --- a/internal/controller/user_controller.go +++ b/internal/controller/user_controller.go @@ -607,3 +607,22 @@ func (uc *UserController) UserUnsubscribeEmailNotification(ctx *gin.Context) { err := uc.userService.UserUnsubscribeEmailNotification(ctx, req) handler.HandleResponse(ctx, err, nil) } + +// SearchUserListByName godoc +// @Summary SearchUserListByName +// @Description SearchUserListByName +// @Tags User +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param username query string true "username" +// @Success 200 {object} handler.RespBody{data=schema.GetOtherUserInfoResp} +// @Router /answer/api/v1/user/info/search [get] +func (uc *UserController) SearchUserListByName(ctx *gin.Context) { + req := &schema.GetOtherUserInfoByUsernameReq{} + if handler.BindAndCheck(ctx, req) { + return + } + resp, err := uc.userService.SearchUserListByName(ctx, req.Username) + handler.HandleResponse(ctx, err, resp) +} diff --git a/internal/repo/user/user_repo.go b/internal/repo/user/user_repo.go index b26bcaa1..9fd52702 100644 --- a/internal/repo/user/user_repo.go +++ b/internal/repo/user/user_repo.go @@ -226,6 +226,22 @@ func (ur *userRepo) GetUserCount(ctx context.Context) (count int64, err error) { return } +func (ur *userRepo) SearchUserListByName(ctx context.Context, name string) (userList []*entity.User, err error) { + userList = make([]*entity.User, 0) + if name == "" { + return userList, nil + } + session := ur.data.DB.Where("") + session.Where("username LIKE LOWER(?) or display_name LIKE ?", name+"%", name+"%").And("status =?", entity.UserStatusAvailable) + session.Asc("username") + session = session.Limit(5, 0) + err = session.OrderBy("id desc").Find(&userList) + if err != nil { + err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } + return +} + func tryToDecorateUserInfoFromUserCenter(ctx context.Context, data *data.Data, original *entity.User) (err error) { if original == nil { return nil diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index a7c2dab9..5c3f60d6 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -112,6 +112,7 @@ func (a *AnswerAPIRouter) RegisterMustUnAuthAnswerAPIRouter(r *gin.RouterGroup) routerGroup.POST("/user/password/reset", a.userController.RetrievePassWord) routerGroup.POST("/user/password/replacement", a.userController.UseRePassWord) routerGroup.PUT("/user/email/notification", a.userController.UserUnsubscribeEmailNotification) + routerGroup.GET("/user/info/search", a.userController.SearchUserListByName) } func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) { diff --git a/internal/service/user_common/user.go b/internal/service/user_common/user.go index 54f4bf64..b6331e70 100644 --- a/internal/service/user_common/user.go +++ b/internal/service/user_common/user.go @@ -35,6 +35,7 @@ type UserRepo interface { GetByUsernames(ctx context.Context, usernames []string) ([]*entity.User, error) GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error) GetUserCount(ctx context.Context) (count int64, err error) + SearchUserListByName(ctx context.Context, name string) (userList []*entity.User, err error) } // UserCommon user service diff --git a/internal/service/user_service.go b/internal/service/user_service.go index 00e6b191..c6ca63c2 100644 --- a/internal/service/user_service.go +++ b/internal/service/user_service.go @@ -814,6 +814,19 @@ func (us *UserService) getUserInfoMapping(ctx context.Context, userIDs []string) return userInfoMapping, nil } +func (us *UserService) SearchUserListByName(ctx context.Context, name string) ([]*schema.UserBasicInfo, error) { + userinfolist := make([]*schema.UserBasicInfo, 0) + list, err := us.userRepo.SearchUserListByName(ctx, name) + if err != nil { + return userinfolist, err + } + for _, user := range list { + userinfo := us.userCommonService.FormatUserBasicInfo(ctx, user) + userinfolist = append(userinfolist, userinfo) + } + return userinfolist, nil +} + func (us *UserService) warpStatRankingResp( userInfoMapping map[string]*entity.User, rankStat []*entity.ActivityUserRankStat, From 4d8bdd109ec107ae09c2cd60d0ab17af4f991c9d Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 23 May 2023 16:00:19 +0800 Subject: [PATCH 04/14] update invite user --- docs/docs.go | 94 ++++++++++++++++++++ docs/swagger.json | 94 ++++++++++++++++++++ docs/swagger.yaml | 59 ++++++++++++ internal/controller/question_controller.go | 83 +++++++++++++++++ internal/router/answer_api_router.go | 2 + internal/schema/question_schema.go | 7 ++ internal/service/question_common/question.go | 30 ++++++- internal/service/question_service.go | 60 ++++++++----- 8 files changed, 404 insertions(+), 25 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 77a19c21..47eb274e 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -3654,6 +3654,83 @@ const docTemplate = `{ } } }, + "/answer/api/v1/question/invite_user": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get question invite user info", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Question" + ], + "summary": "get question invite user info", + "parameters": [ + { + "type": "string", + "default": "1", + "description": "Question ID", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/answer/api/v1/question/inviter_user": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update question invite user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Question" + ], + "summary": "update question invite user", + "parameters": [ + { + "description": "question", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.QuestionUpdateInviteUser" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/api/v1/question/operation": { "put": { "security": [ @@ -7857,6 +7934,23 @@ const docTemplate = `{ } } }, + "schema.QuestionUpdateInviteUser": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "invite_user": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "schema.RemoveAnswerReq": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index 31fdf372..a3c2e8e6 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -3642,6 +3642,83 @@ } } }, + "/answer/api/v1/question/invite_user": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get question invite user info", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Question" + ], + "summary": "get question invite user info", + "parameters": [ + { + "type": "string", + "default": "1", + "description": "Question ID", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/answer/api/v1/question/inviter_user": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update question invite user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Question" + ], + "summary": "update question invite user", + "parameters": [ + { + "description": "question", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.QuestionUpdateInviteUser" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/api/v1/question/operation": { "put": { "security": [ @@ -7845,6 +7922,23 @@ } } }, + "schema.QuestionUpdateInviteUser": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "invite_user": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "schema.RemoveAnswerReq": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index b3ef8a9d..88f3e18a 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1358,6 +1358,17 @@ definitions: - tags - title type: object + schema.QuestionUpdateInviteUser: + properties: + id: + type: string + invite_user: + items: + type: string + type: array + required: + - id + type: object schema.RemoveAnswerReq: properties: id: @@ -4481,6 +4492,54 @@ paths: summary: get question details tags: - Question + /answer/api/v1/question/invite_user: + get: + consumes: + - application/json + description: get question invite user info + parameters: + - default: "1" + description: Question ID + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + security: + - ApiKeyAuth: [] + summary: get question invite user info + tags: + - Question + /answer/api/v1/question/inviter_user: + put: + consumes: + - application/json + description: update question invite user + parameters: + - description: question + in: body + name: data + required: true + schema: + $ref: '#/definitions/schema.QuestionUpdateInviteUser' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.RespBody' + security: + - ApiKeyAuth: [] + summary: update question invite user + tags: + - Question /answer/api/v1/question/operation: put: consumes: diff --git a/internal/controller/question_controller.go b/internal/controller/question_controller.go index e72a692e..107c469f 100644 --- a/internal/controller/question_controller.go +++ b/internal/controller/question_controller.go @@ -221,6 +221,44 @@ func (qc *QuestionController) GetQuestion(ctx *gin.Context) { handler.HandleResponse(ctx, nil, info) } +// GetQuestionInviteUserInfo get question invite user info +// @Summary get question invite user info +// @Description get question invite user info +// @Tags Question +// @Security ApiKeyAuth +// @Accept json +// @Produce json +// @Param id query string true "Question ID" default(1) +// @Success 200 {string} string "" +// @Router /answer/api/v1/question/invite_user [get] +func (qc *QuestionController) GetQuestionInviteUserInfo(ctx *gin.Context) { + id := ctx.Query("id") + id = uid.DeShortID(id) + userID := middleware.GetLoginUserIDFromContext(ctx) + req := schema.QuestionPermission{} + canList, err := qc.rankService.CheckOperationPermissions(ctx, userID, []string{ + permission.QuestionEdit, + }) + if err != nil { + handler.HandleResponse(ctx, err, nil) + return + } + objectOwner := qc.rankService.CheckOperationObjectOwner(ctx, userID, id) + + req.CanEdit = canList[0] || objectOwner + if !req.CanEdit { + handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil) + return + } + list, err := qc.questionService.InviteUserInfo(ctx, id) + if err != nil { + handler.HandleResponse(ctx, err, nil) + return + } + handler.HandleResponse(ctx, nil, list) + +} + // SimilarQuestion godoc // @Summary Search Similar Question // @Description Search Similar Question @@ -500,6 +538,51 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) { handler.HandleResponse(ctx, nil, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview}) } +// UpdateQuestionInviteUser update question invite user +// @Summary update question invite user +// @Description update question invite user +// @Tags Question +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param data body schema.QuestionUpdateInviteUser true "question" +// @Success 200 {object} handler.RespBody +// @Router /answer/api/v1/question/inviter_user [put] +func (qc *QuestionController) UpdateQuestionInviteUser(ctx *gin.Context) { + req := &schema.QuestionUpdateInviteUser{} + errFields := handler.BindAndCheckReturnErr(ctx, req) + if ctx.IsAborted() { + return + } + req.ID = uid.DeShortID(req.ID) + req.UserID = middleware.GetLoginUserIDFromContext(ctx) + + canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{ + permission.QuestionEdit, + }) + if err != nil { + handler.HandleResponse(ctx, err, nil) + return + } + + objectOwner := qc.rankService.CheckOperationObjectOwner(ctx, req.UserID, req.ID) + req.CanEdit = canList[0] || objectOwner + if !req.CanEdit { + handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil) + return + } + if len(errFields) > 0 { + handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields) + return + } + err = qc.questionService.UpdateQuestionInviteUser(ctx, req) + if err != nil { + handler.HandleResponse(ctx, err, nil) + return + } + handler.HandleResponse(ctx, nil, nil) +} + // CloseMsgList close question msg list // @Summary close question msg list // @Description close question msg list diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index 5c3f60d6..3f90218c 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -130,6 +130,7 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) { //question r.GET("/question/info", a.questionController.GetQuestion) + r.GET("/question/invite_user", a.questionController.GetQuestionInviteUserInfo) r.GET("/question/page", a.questionController.QuestionPage) r.GET("/question/similar/tag", a.questionController.SimilarQuestion) r.GET("/personal/qa/top", a.questionController.UserTop) @@ -194,6 +195,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) { r.POST("/question", a.questionController.AddQuestion) r.POST("/question/answer", a.questionController.AddQuestionByAnswer) r.PUT("/question", a.questionController.UpdateQuestion) + r.PUT("/question/inviter_user", a.questionController.UpdateQuestionInviteUser) r.DELETE("/question", a.questionController.RemoveQuestion) r.PUT("/question/status", a.questionController.CloseQuestion) r.PUT("/question/operation", a.questionController.OperationQuestion) diff --git a/internal/schema/question_schema.go b/internal/schema/question_schema.go index 9ef45d7a..3f0b518c 100644 --- a/internal/schema/question_schema.go +++ b/internal/schema/question_schema.go @@ -152,6 +152,13 @@ type QuestionUpdate struct { QuestionPermission } +type QuestionUpdateInviteUser struct { + ID string `validate:"required" json:"id"` + InviteUser []string `validate:"omitempty" json:"invite_user"` + UserID string `json:"-"` + QuestionPermission +} + func (req *QuestionUpdate) Check() (errFields []*validator.FormErrorField, err error) { req.HTML = converter.Markdown2HTML(req.Content) return nil, nil diff --git a/internal/service/question_common/question.go b/internal/service/question_common/question.go index 46a76067..26eb2cc6 100644 --- a/internal/service/question_common/question.go +++ b/internal/service/question_common/question.go @@ -150,6 +150,34 @@ func (qs *QuestionCommon) FindInfoByID(ctx context.Context, questionIDs []string return list, nil } +func (qs *QuestionCommon) InviteUserInfo(ctx context.Context, questionID string) (inviteList []*schema.UserBasicInfo, err error) { + InviteUserInfo := make([]*schema.UserBasicInfo, 0) + dbinfo, has, err := qs.questionRepo.GetQuestion(ctx, questionID) + if err != nil { + return InviteUserInfo, err + } + if !has { + return InviteUserInfo, errors.NotFound(reason.QuestionNotFound) + } + //InviteUser + if dbinfo.InviteUserID != "" { + InviteUserIDs := make([]string, 0) + err := json.Unmarshal([]byte(dbinfo.InviteUserID), &InviteUserIDs) + if err == nil { + inviteUserInfoMap, err := qs.userCommon.BatchUserBasicInfoByID(ctx, InviteUserIDs) + if err == nil { + for _, userid := range InviteUserIDs { + _, ok := inviteUserInfoMap[userid] + if ok { + InviteUserInfo = append(InviteUserInfo, inviteUserInfoMap[userid]) + } + } + } + } + } + return InviteUserInfo, nil +} + func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUserID string) (showinfo *schema.QuestionInfo, err error) { dbinfo, has, err := qs.questionRepo.GetQuestion(ctx, questionID) if err != nil { @@ -186,9 +214,7 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUser operation.Level = schema.OperationLevelInfo showinfo.Operation = operation } - } - } } diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 73c89386..32b24a5e 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -550,6 +550,38 @@ func (qs *QuestionService) UpdateQuestionCheckTags(ctx context.Context, req *sch return nil, nil } +func (qs *QuestionService) UpdateQuestionInviteUser(ctx context.Context, req *schema.QuestionUpdateInviteUser) (err error) { + //verify invite user + inviteUserInfoList, err := qs.userCommon.BatchGetUserBasicInfoByUserNames(ctx, req.InviteUser) + if err != nil { + log.Error("BatchGetUserBasicInfoByUserNames error", err.Error()) + } + inviteUser := make([]string, 0) + for _, item := range req.InviteUser { + _, ok := inviteUserInfoList[item] + if ok { + inviteUser = append(inviteUser, inviteUserInfoList[item].ID) + } + } + inviteUserStr := "" + inviteUserByte, err := json.Marshal(inviteUser) + if err != nil { + log.Error("json.Marshal error", err.Error()) + inviteUserStr = "[]" + } else { + inviteUserStr = string(inviteUserByte) + } + question := &entity.Question{} + question.ID = uid.DeShortID(req.ID) + question.InviteUserID = inviteUserStr + + saveerr := qs.questionRepo.UpdateQuestion(ctx, question, []string{"invite_user_id"}) + if saveerr != nil { + return saveerr + } + return nil +} + // UpdateQuestion update question func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.QuestionUpdate) (questionInfo any, err error) { var canUpdate bool @@ -577,27 +609,6 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest return nil, err } - //verify invite user - inviteUserInfoList, err := qs.userCommon.BatchGetUserBasicInfoByUserNames(ctx, req.InviteUser) - if err != nil { - log.Error("BatchGetUserBasicInfoByUserNames error", err.Error()) - } - inviteUser := make([]string, 0) - for _, item := range req.InviteUser { - _, ok := inviteUserInfoList[item] - if ok { - inviteUser = append(inviteUser, inviteUserInfoList[item].ID) - } - } - inviteUserStr := "" - inviteUserByte, err := json.Marshal(inviteUser) - if err != nil { - log.Error("json.Marshal error", err.Error()) - inviteUserStr = "[]" - } else { - inviteUserStr = string(inviteUserByte) - } - now := time.Now() question := &entity.Question{} question.Title = req.Title @@ -605,7 +616,6 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest question.ParsedText = req.HTML question.ID = uid.DeShortID(req.ID) question.UpdatedAt = now - question.InviteUserID = inviteUserStr question.PostUpdateTime = now question.UserID = dbinfo.UserID question.LastEditUserID = req.UserID @@ -699,7 +709,7 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest //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", "invite_user_id"}) + 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 } @@ -793,6 +803,10 @@ func (qs *QuestionService) GetQuestionAndAddPV(ctx context.Context, questionID, return qs.GetQuestion(ctx, questionID, loginUserID, per) } +func (qs *QuestionService) InviteUserInfo(ctx context.Context, questionID string) (inviteList []*schema.UserBasicInfo, err error) { + return qs.questioncommon.InviteUserInfo(ctx, questionID) +} + func (qs *QuestionService) ChangeTag(ctx context.Context, objectTagData *schema.TagChange) error { return qs.tagCommon.ObjectChangeTag(ctx, objectTagData) } From c777fec109aa1a7c9edb0a96f14894a05d02f6a0 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 23 May 2023 16:04:04 +0800 Subject: [PATCH 05/14] update invite user --- internal/repo/user/user_repo.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/repo/user/user_repo.go b/internal/repo/user/user_repo.go index 9fd52702..97be3837 100644 --- a/internal/repo/user/user_repo.go +++ b/internal/repo/user/user_repo.go @@ -239,6 +239,7 @@ func (ur *userRepo) SearchUserListByName(ctx context.Context, name string) (user if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } + tryToDecorateUserListFromUserCenter(ctx, ur.data, userList) return } From f5bb3ff6e54ba898ab9a4dc1efac41c583906953 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 23 May 2023 16:07:43 +0800 Subject: [PATCH 06/14] update invite user --- docs/docs.go | 6 ++---- docs/swagger.json | 6 ++---- docs/swagger.yaml | 3 +-- internal/controller/question_controller.go | 4 ++-- internal/router/answer_api_router.go | 4 ++-- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 47eb274e..ad4e2c58 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -3654,7 +3654,7 @@ const docTemplate = `{ } } }, - "/answer/api/v1/question/invite_user": { + "/answer/api/v1/question/invite": { "get": { "security": [ { @@ -3690,9 +3690,7 @@ const docTemplate = `{ } } } - } - }, - "/answer/api/v1/question/inviter_user": { + }, "put": { "security": [ { diff --git a/docs/swagger.json b/docs/swagger.json index a3c2e8e6..3312fc5e 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -3642,7 +3642,7 @@ } } }, - "/answer/api/v1/question/invite_user": { + "/answer/api/v1/question/invite": { "get": { "security": [ { @@ -3678,9 +3678,7 @@ } } } - } - }, - "/answer/api/v1/question/inviter_user": { + }, "put": { "security": [ { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 88f3e18a..8519dc50 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -4492,7 +4492,7 @@ paths: summary: get question details tags: - Question - /answer/api/v1/question/invite_user: + /answer/api/v1/question/invite: get: consumes: - application/json @@ -4516,7 +4516,6 @@ paths: summary: get question invite user info tags: - Question - /answer/api/v1/question/inviter_user: put: consumes: - application/json diff --git a/internal/controller/question_controller.go b/internal/controller/question_controller.go index 107c469f..9ab267ef 100644 --- a/internal/controller/question_controller.go +++ b/internal/controller/question_controller.go @@ -230,7 +230,7 @@ func (qc *QuestionController) GetQuestion(ctx *gin.Context) { // @Produce json // @Param id query string true "Question ID" default(1) // @Success 200 {string} string "" -// @Router /answer/api/v1/question/invite_user [get] +// @Router /answer/api/v1/question/invite [get] func (qc *QuestionController) GetQuestionInviteUserInfo(ctx *gin.Context) { id := ctx.Query("id") id = uid.DeShortID(id) @@ -547,7 +547,7 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) { // @Security ApiKeyAuth // @Param data body schema.QuestionUpdateInviteUser true "question" // @Success 200 {object} handler.RespBody -// @Router /answer/api/v1/question/inviter_user [put] +// @Router /answer/api/v1/question/invite [put] func (qc *QuestionController) UpdateQuestionInviteUser(ctx *gin.Context) { req := &schema.QuestionUpdateInviteUser{} errFields := handler.BindAndCheckReturnErr(ctx, req) diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index 3f90218c..ccc46909 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -130,7 +130,7 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) { //question r.GET("/question/info", a.questionController.GetQuestion) - r.GET("/question/invite_user", a.questionController.GetQuestionInviteUserInfo) + r.GET("/question/invite", a.questionController.GetQuestionInviteUserInfo) r.GET("/question/page", a.questionController.QuestionPage) r.GET("/question/similar/tag", a.questionController.SimilarQuestion) r.GET("/personal/qa/top", a.questionController.UserTop) @@ -195,7 +195,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) { r.POST("/question", a.questionController.AddQuestion) r.POST("/question/answer", a.questionController.AddQuestionByAnswer) r.PUT("/question", a.questionController.UpdateQuestion) - r.PUT("/question/inviter_user", a.questionController.UpdateQuestionInviteUser) + r.PUT("/question/invite", a.questionController.UpdateQuestionInviteUser) r.DELETE("/question", a.questionController.RemoveQuestion) r.PUT("/question/status", a.questionController.CloseQuestion) r.PUT("/question/operation", a.questionController.OperationQuestion) From 15438f040310ff5b240b1416b2304aedb85c57fe Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 23 May 2023 16:32:10 +0800 Subject: [PATCH 07/14] feat(notification): add notification for invited to answer --- internal/base/constant/notification.go | 2 ++ internal/service/question_service.go | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/internal/base/constant/notification.go b/internal/base/constant/notification.go index 8f8568a8..165c4c01 100644 --- a/internal/base/constant/notification.go +++ b/internal/base/constant/notification.go @@ -35,4 +35,6 @@ const ( NotificationYourAnswerWasDeleted = "notification.action.your_answer_was_deleted" // NotificationYourCommentWasDeleted your comment was deleted NotificationYourCommentWasDeleted = "notification.action.your_comment_was_deleted" + // NotificationInvitedYouToAnswer invited you to answer + NotificationInvitedYouToAnswer = "notification.action.invited_you_to_answer" ) diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 32b24a5e..5ec5f99d 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -579,9 +579,25 @@ func (qs *QuestionService) UpdateQuestionInviteUser(ctx context.Context, req *sc if saveerr != nil { return saveerr } + qs.notificationInviteUser(ctx, inviteUser, req.ID, req.UserID) return nil } +func (qs *QuestionService) notificationInviteUser( + ctx context.Context, invitedUserIDs []string, questionID, questionUserID string) { + for _, userID := range invitedUserIDs { + msg := &schema.NotificationMsg{ + ReceiverUserID: userID, + TriggerUserID: questionUserID, + Type: schema.NotificationTypeInbox, + ObjectID: questionID, + } + msg.ObjectType = constant.QuestionObjectType + msg.NotificationAction = constant.NotificationInvitedYouToAnswer + notice_queue.AddNotification(msg) + } +} + // UpdateQuestion update question func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.QuestionUpdate) (questionInfo any, err error) { var canUpdate bool From 3425d223ac600c526895ac8689b3294953db7100 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 23 May 2023 17:20:00 +0800 Subject: [PATCH 08/14] update config --- .vscode/extensions.json | 5 ----- .vscode/settings.json | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 28d95959..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "recommendations": [ - "github.copilot" - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json index 6c384d1e..93106f18 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,5 @@ { "eslint.workingDirectories": [ "ui" - ], - "commentTranslate.multiLineMerge": true + ] } From 674d98e9838c616cb9f5edacbcbe73811e1d0263 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 23 May 2023 17:23:56 +0800 Subject: [PATCH 09/14] feat(notification): add notification translation --- i18n/en_US.yaml | 2 ++ internal/controller/question_controller.go | 29 +++------------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index ff9fd7ca..901ec26a 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -388,6 +388,8 @@ backend: other: downvoted answer up_voted_comment: other: upvoted comment + invited_you_to_answer: + other: invited you to answer email_tpl: change_email: title: diff --git a/internal/controller/question_controller.go b/internal/controller/question_controller.go index 813809c8..77e85535 100644 --- a/internal/controller/question_controller.go +++ b/internal/controller/question_controller.go @@ -232,30 +232,9 @@ func (qc *QuestionController) GetQuestion(ctx *gin.Context) { // @Success 200 {string} string "" // @Router /answer/api/v1/question/invite [get] func (qc *QuestionController) GetQuestionInviteUserInfo(ctx *gin.Context) { - id := ctx.Query("id") - id = uid.DeShortID(id) - userID := middleware.GetLoginUserIDFromContext(ctx) - req := schema.QuestionPermission{} - canList, err := qc.rankService.CheckOperationPermissions(ctx, userID, []string{ - permission.QuestionEdit, - }) - if err != nil { - handler.HandleResponse(ctx, err, nil) - return - } - objectOwner := qc.rankService.CheckOperationObjectOwner(ctx, userID, id) - - req.CanEdit = canList[0] || objectOwner - if !req.CanEdit { - handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil) - return - } - list, err := qc.questionService.InviteUserInfo(ctx, id) - if err != nil { - handler.HandleResponse(ctx, err, nil) - return - } - handler.HandleResponse(ctx, nil, list) + questionID := uid.DeShortID(ctx.Query("id")) + resp, err := qc.questionService.InviteUserInfo(ctx, questionID) + handler.HandleResponse(ctx, err, resp) } @@ -558,7 +537,7 @@ func (qc *QuestionController) UpdateQuestionInviteUser(ctx *gin.Context) { req.UserID = middleware.GetLoginUserIDFromContext(ctx) canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{ - permission.QuestionEdit, + permission.AnswerInviteSomeoneToAnswer, }) if err != nil { handler.HandleResponse(ctx, err, nil) From bd5ea55ac802e27842b9fecee0600b533e989339 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 23 May 2023 17:32:23 +0800 Subject: [PATCH 10/14] Upgrade v12 --- internal/migrations/v12.go | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/internal/migrations/v12.go b/internal/migrations/v12.go index 34b6c0c4..2107228b 100644 --- a/internal/migrations/v12.go +++ b/internal/migrations/v12.go @@ -2,14 +2,44 @@ package migrations import ( "fmt" + "time" "github.com/answerdev/answer/internal/entity" "github.com/segmentfault/pacman/log" "xorm.io/xorm" ) +type QuestionPostTime struct { + ID string `xorm:"not null pk BIGINT(20) id"` + CreatedAt time.Time `xorm:"not null default CURRENT_TIMESTAMP TIMESTAMP created_at"` + UpdatedAt time.Time `xorm:"updated_at TIMESTAMP"` + UserID string `xorm:"not null default 0 BIGINT(20) INDEX user_id"` + InviteUserID string `xorm:"TEXT invite_user_id"` + LastEditUserID string `xorm:"not null default 0 BIGINT(20) last_edit_user_id"` + Title string `xorm:"not null default '' VARCHAR(150) title"` + OriginalText string `xorm:"not null MEDIUMTEXT original_text"` + ParsedText string `xorm:"not null MEDIUMTEXT parsed_text"` + Status int `xorm:"not null default 1 INT(11) status"` + Pin int `xorm:"not null default 1 INT(11) pin"` + Show int `xorm:"not null default 1 INT(11) show"` + ViewCount int `xorm:"not null default 0 INT(11) view_count"` + UniqueViewCount int `xorm:"not null default 0 INT(11) unique_view_count"` + VoteCount int `xorm:"not null default 0 INT(11) vote_count"` + AnswerCount int `xorm:"not null default 0 INT(11) answer_count"` + CollectionCount int `xorm:"not null default 0 INT(11) collection_count"` + FollowCount int `xorm:"not null default 0 INT(11) follow_count"` + AcceptedAnswerID string `xorm:"not null default 0 BIGINT(20) accepted_answer_id"` + LastAnswerID string `xorm:"not null default 0 BIGINT(20) last_answer_id"` + PostUpdateTime time.Time `xorm:"post_update_time TIMESTAMP"` + RevisionID string `xorm:"not null default 0 BIGINT(20) revision_id"` +} + +func (QuestionPostTime) TableName() string { + return "question" +} + func updateQuestionPostTime(x *xorm.Engine) error { - questionList := make([]entity.Question, 0) + questionList := make([]QuestionPostTime, 0) err := x.Find(&questionList, &entity.Question{}) if err != nil { return fmt.Errorf("get questions failed: %w", err) @@ -21,7 +51,7 @@ func updateQuestionPostTime(x *xorm.Engine) error { } else if !item.CreatedAt.IsZero() { item.PostUpdateTime = item.CreatedAt } - if _, err = x.Update(item, &entity.Question{ID: item.ID}); err != nil { + if _, err = x.Update(item, &QuestionPostTime{ID: item.ID}); err != nil { log.Errorf("update %+v config failed: %s", item, err) return fmt.Errorf("update question failed: %w", err) } From e12e0094d2cf4860435c1d0e0e0d69ba9a5b89de Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Wed, 24 May 2023 10:35:38 +0800 Subject: [PATCH 11/14] feat(notification): add email notification for invited to answer --- cmd/wire_gen.go | 2 +- i18n/en_US.yaml | 5 ++ i18n/zh_CN.yaml | 11 +++- internal/base/constant/email_tpl_key.go | 3 + internal/schema/email_template.go | 15 +++++ internal/service/export/email_service.go | 23 ++++++- internal/service/question_service.go | 81 ++++++++++++++++++++++-- 7 files changed, 129 insertions(+), 11 deletions(-) diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index e88de43f..f50f34a2 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -170,7 +170,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, answerActivityRepo := activity.NewAnswerActivityRepo(dataData, activityRepo, userRankRepo) questionActivityRepo := activity.NewQuestionActivityRepo(dataData, activityRepo, userRankRepo) answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, questionActivityRepo) - questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, revisionService, metaService, collectionCommon, answerActivityService, dataData) + questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, userRepo, revisionService, metaService, collectionCommon, answerActivityService, dataData, emailService) answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService) questionController := controller.NewQuestionController(questionService, answerService, rankService) dashboardService := dashboard.NewDashboardService(questionRepo, answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configRepo, siteInfoCommonService, serviceConf, dataData) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 901ec26a..dbfbb39a 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -401,6 +401,11 @@ backend: other: "[{{.SiteName}}] {{.DisplayName}} answered your question" body: other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.AnswerSummary}}

\nView it on {{.SiteName}}

\n\nYou are receiving this because you authored the thread. Unsubscribe" + invited_you_to_answer: + title: + other: "[{{.SiteName}}] {{.DisplayName}} invited you to answer" + body: + other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
I think you may know the answer.

\nView it on {{.SiteName}}

\n\nYou are receiving this because you authored the thread. Unsubscribe" new_comment: title: other: "[{{.SiteName}}] {{.DisplayName}} commented on your post" diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index ca2edf49..6329718e 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -378,6 +378,8 @@ backend: other: 踩了答案 up_voted_comment: other: 赞了评论 + invited_you_to_answer: + other: 邀请你回答问题 email_tpl: change_email: title: @@ -388,12 +390,17 @@ backend: title: other: "[{{.SiteName}}] {{.DisplayName}} 回答了您的问题" body: - other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.AnswerSummary}}

\n在 {{.SiteName}} 上查看

\n\n您会收到此邮件是因为您是该讨论的作者。取消订阅" + other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.AnswerSummary}}

\n在 {{.SiteName}} 上查看

\n\n您会收到此邮件是因为您开启了订阅。取消订阅" + invited_you_to_answer: + title: + other: "[{{.SiteName}}] {{.DisplayName}} 邀请您回答问题" + body: + other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
我想你可能知道答案。

\n在 {{.SiteName}} 上查看

\n\n您会收到此邮件是因为您开启了订阅. 取消订阅" new_comment: title: other: "[{{.SiteName}}] {{.DisplayName}} 评论了您的帖子" body: - other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.CommentSummary}}

\n在 {{.SiteName}} 上查看

\n\n您会收到此邮件是因为您是该讨论的作者。取消订阅" + other: "{{.QuestionTitle}}

\n\n{{.DisplayName}}:
\n
{{.CommentSummary}}

\n在 {{.SiteName}} 上查看

\n\n您会收到此邮件是因为您开启了订阅。取消订阅" pass_reset: title: other: "[{{.SiteName }}] 重置密码" diff --git a/internal/base/constant/email_tpl_key.go b/internal/base/constant/email_tpl_key.go index 178f2d30..cb191165 100644 --- a/internal/base/constant/email_tpl_key.go +++ b/internal/base/constant/email_tpl_key.go @@ -18,4 +18,7 @@ const ( EmailTplKeyTestTitle = "email_tpl.test.title" EmailTplKeyTestBody = "email_tpl.test.body" + + EmailTplKeyInvitedAnswerTitle = "email_tpl.invited_you_to_answer.title" + EmailTplKeyInvitedAnswerBody = "email_tpl.invited_you_to_answer.body" ) diff --git a/internal/schema/email_template.go b/internal/schema/email_template.go index 94918331..01cc1667 100644 --- a/internal/schema/email_template.go +++ b/internal/schema/email_template.go @@ -47,6 +47,21 @@ type NewAnswerTemplateData struct { UnsubscribeUrl string } +type NewInviteAnswerTemplateRawData struct { + InviterDisplayName string + QuestionTitle string + QuestionID string + UnsubscribeCode string +} + +type NewInviteAnswerTemplateData struct { + SiteName string + DisplayName string + QuestionTitle string + InviteUrl string + UnsubscribeUrl string +} + type NewCommentTemplateRawData struct { CommentUserDisplayName string QuestionTitle string diff --git a/internal/service/export/email_service.go b/internal/service/export/email_service.go index 63b26598..4fd2fa9f 100644 --- a/internal/service/export/email_service.go +++ b/internal/service/export/email_service.go @@ -242,7 +242,6 @@ func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAn AnswerSummary: raw.AnswerSummary, UnsubscribeUrl: fmt.Sprintf("%s/users/unsubscribe?code=%s", siteInfo.SiteUrl, raw.UnsubscribeCode), } - templateData.SiteName = siteInfo.Name lang := handler.GetLangByCtx(ctx) title = translator.TrWithData(lang, constant.EmailTplKeyNewAnswerTitle, templateData) @@ -250,6 +249,27 @@ func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAn return title, body, nil } +// NewInviteAnswerTemplate new invite answer template +func (es *EmailService) NewInviteAnswerTemplate(ctx context.Context, raw *schema.NewInviteAnswerTemplateRawData) ( + title, body string, err error) { + siteInfo, err := es.GetSiteGeneral(ctx) + if err != nil { + return + } + templateData := &schema.NewInviteAnswerTemplateData{ + SiteName: siteInfo.Name, + DisplayName: raw.InviterDisplayName, + QuestionTitle: raw.QuestionTitle, + InviteUrl: fmt.Sprintf("%s/questions/%s", siteInfo.SiteUrl, raw.QuestionID), + UnsubscribeUrl: fmt.Sprintf("%s/users/unsubscribe?code=%s", siteInfo.SiteUrl, raw.UnsubscribeCode), + } + + lang := handler.GetLangByCtx(ctx) + title = translator.TrWithData(lang, constant.EmailTplKeyInvitedAnswerTitle, templateData) + body = translator.TrWithData(lang, constant.EmailTplKeyInvitedAnswerBody, templateData) + return title, body, nil +} + // NewCommentTemplate new comment template func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewCommentTemplateRawData) ( title, body string, err error) { @@ -271,7 +291,6 @@ func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewC templateData.CommentUrl = fmt.Sprintf("%s/questions/%s?commentId=%s", siteInfo.SiteUrl, raw.QuestionID, raw.CommentID) } - templateData.SiteName = siteInfo.Name lang := handler.GetLangByCtx(ctx) title = translator.TrWithData(lang, constant.EmailTplKeyNewCommentTitle, templateData) diff --git a/internal/service/question_service.go b/internal/service/question_service.go index a705265f..a2ae66ba 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -19,6 +19,7 @@ import ( "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/export" "github.com/answerdev/answer/internal/service/meta" "github.com/answerdev/answer/internal/service/notice_queue" "github.com/answerdev/answer/internal/service/permission" @@ -26,10 +27,12 @@ import ( "github.com/answerdev/answer/internal/service/revision_common" tagcommon "github.com/answerdev/answer/internal/service/tag_common" usercommon "github.com/answerdev/answer/internal/service/user_common" + "github.com/answerdev/answer/pkg/encryption" "github.com/answerdev/answer/pkg/htmltext" "github.com/answerdev/answer/pkg/uid" "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" + "github.com/segmentfault/pacman/i18n" "github.com/segmentfault/pacman/log" "golang.org/x/net/context" ) @@ -42,11 +45,13 @@ type QuestionService struct { tagCommon *tagcommon.TagCommonService questioncommon *questioncommon.QuestionCommon userCommon *usercommon.UserCommon + userRepo usercommon.UserRepo revisionService *revision_common.RevisionService metaService *meta.MetaService collectionCommon *collectioncommon.CollectionCommon answerActivityService *activity.AnswerActivityService data *data.Data + emailService *export.EmailService } func NewQuestionService( @@ -54,23 +59,26 @@ func NewQuestionService( tagCommon *tagcommon.TagCommonService, questioncommon *questioncommon.QuestionCommon, userCommon *usercommon.UserCommon, + userRepo usercommon.UserRepo, revisionService *revision_common.RevisionService, metaService *meta.MetaService, collectionCommon *collectioncommon.CollectionCommon, answerActivityService *activity.AnswerActivityService, data *data.Data, - + emailService *export.EmailService, ) *QuestionService { return &QuestionService{ questionRepo: questionRepo, tagCommon: tagCommon, questioncommon: questioncommon, userCommon: userCommon, + userRepo: userRepo, revisionService: revisionService, metaService: metaService, collectionCommon: collectionCommon, answerActivityService: answerActivityService, data: data, + emailService: emailService, } } @@ -536,20 +544,28 @@ func (qs *QuestionService) UpdateQuestionCheckTags(ctx context.Context, req *sch } func (qs *QuestionService) UpdateQuestionInviteUser(ctx context.Context, req *schema.QuestionUpdateInviteUser) (err error) { + originQuestion, exist, err := qs.questionRepo.GetQuestion(ctx, req.ID) + if err != nil { + return err + } + if !exist { + return errors.NotFound(reason.ObjectNotFound) + } + //verify invite user inviteUserInfoList, err := qs.userCommon.BatchGetUserBasicInfoByUserNames(ctx, req.InviteUser) if err != nil { log.Error("BatchGetUserBasicInfoByUserNames error", err.Error()) } - inviteUser := make([]string, 0) + inviteUserIDs := make([]string, 0) for _, item := range req.InviteUser { _, ok := inviteUserInfoList[item] if ok { - inviteUser = append(inviteUser, inviteUserInfoList[item].ID) + inviteUserIDs = append(inviteUserIDs, inviteUserInfoList[item].ID) } } inviteUserStr := "" - inviteUserByte, err := json.Marshal(inviteUser) + inviteUserByte, err := json.Marshal(inviteUserIDs) if err != nil { log.Error("json.Marshal error", err.Error()) inviteUserStr = "[]" @@ -564,12 +580,31 @@ func (qs *QuestionService) UpdateQuestionInviteUser(ctx context.Context, req *sc if saveerr != nil { return saveerr } - qs.notificationInviteUser(ctx, inviteUser, req.ID, req.UserID) + go qs.notificationInviteUser(ctx, inviteUserIDs, originQuestion.ID, originQuestion.Title, req.UserID) return nil } func (qs *QuestionService) notificationInviteUser( - ctx context.Context, invitedUserIDs []string, questionID, questionUserID string) { + ctx context.Context, invitedUserIDs []string, questionID, questionTitle, questionUserID string) { + inviter, exist, err := qs.userCommon.GetUserBasicInfoByID(ctx, questionUserID) + if err != nil { + log.Error(err) + return + } + if !exist { + log.Warnf("user %s not found", questionUserID) + return + } + + users, err := qs.userRepo.BatchGetByID(ctx, invitedUserIDs) + if err != nil { + log.Error(err) + return + } + invitee := make(map[string]*entity.User, len(users)) + for _, user := range users { + invitee[user.ID] = user + } for _, userID := range invitedUserIDs { msg := &schema.NotificationMsg{ ReceiverUserID: userID, @@ -580,6 +615,40 @@ func (qs *QuestionService) notificationInviteUser( msg.ObjectType = constant.QuestionObjectType msg.NotificationAction = constant.NotificationInvitedYouToAnswer notice_queue.AddNotification(msg) + + userInfo, ok := invitee[userID] + if !ok { + log.Warnf("user %s not found", userID) + return + } + if userInfo.NoticeStatus == schema.NoticeStatusOff || len(userInfo.EMail) == 0 { + return + } + + rawData := &schema.NewInviteAnswerTemplateRawData{ + InviterDisplayName: inviter.DisplayName, + QuestionTitle: questionTitle, + QuestionID: questionID, + UnsubscribeCode: encryption.MD5(userInfo.Pass), + } + codeContent := &schema.EmailCodeContent{ + SourceType: schema.UnsubscribeSourceType, + Email: userInfo.EMail, + UserID: userInfo.ID, + } + + // If receiver has set language, use it to send email. + if len(userInfo.Language) > 0 { + ctx = context.WithValue(ctx, constant.AcceptLanguageFlag, i18n.Language(userInfo.Language)) + } + title, body, err := qs.emailService.NewInviteAnswerTemplate(ctx, rawData) + if err != nil { + log.Error(err) + return + } + + go qs.emailService.SendAndSaveCodeWithTime( + ctx, userInfo.EMail, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 7*24*time.Hour) } } From fb86b861da8ddfbbf7e8a4029a25476c17d322be Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 24 May 2023 11:02:16 +0800 Subject: [PATCH 12/14] update inbox --- docs/docs.go | 113 +----------------- docs/swagger.json | 113 +----------------- docs/swagger.yaml | 73 +---------- internal/schema/notification_schema.go | 35 ++++-- .../notification/notification_service.go | 5 + 5 files changed, 45 insertions(+), 294 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index ad4e2c58..4a90dfff 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -3588,34 +3588,6 @@ const docTemplate = `{ } } }, - "/answer/api/v1/question/closemsglist": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "close question msg list", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Question" - ], - "summary": "close question msg list", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.RespBody" - } - } - } - } - }, "/answer/api/v1/question/info": { "get": { "security": [ @@ -4124,56 +4096,6 @@ const docTemplate = `{ } } }, - "/answer/api/v1/report/type/list": { - "get": { - "description": "get report type list", - "produces": [ - "application/json" - ], - "tags": [ - "Report" - ], - "summary": "get report type list", - "parameters": [ - { - "enum": [ - "question", - "answer", - "comment", - "user" - ], - "type": "string", - "description": "report source", - "name": "source", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/handler.RespBody" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/schema.GetReportTypeResp" - } - } - } - } - ] - } - } - } - } - }, "/answer/api/v1/revisions": { "get": { "description": "get revision list", @@ -7003,35 +6925,6 @@ const docTemplate = `{ } } }, - "schema.GetReportTypeResp": { - "type": "object", - "properties": { - "content_type": { - "description": "content type", - "type": "string" - }, - "description": { - "description": "report description", - "type": "string" - }, - "have_content": { - "description": "is have content", - "type": "boolean" - }, - "name": { - "description": "report name", - "type": "string" - }, - "source": { - "description": "report source", - "type": "string" - }, - "type": { - "description": "report type", - "type": "integer" - } - } - }, "schema.GetRevisionResp": { "type": "object", "properties": { @@ -8508,6 +8401,9 @@ const docTemplate = `{ "system", "gravatar" ] + }, + "gravatar_base_url": { + "type": "string" } } }, @@ -8541,6 +8437,9 @@ const docTemplate = `{ "system", "gravatar" ] + }, + "gravatar_base_url": { + "type": "string" } } }, diff --git a/docs/swagger.json b/docs/swagger.json index 3312fc5e..dac3816e 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -3576,34 +3576,6 @@ } } }, - "/answer/api/v1/question/closemsglist": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "close question msg list", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Question" - ], - "summary": "close question msg list", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.RespBody" - } - } - } - } - }, "/answer/api/v1/question/info": { "get": { "security": [ @@ -4112,56 +4084,6 @@ } } }, - "/answer/api/v1/report/type/list": { - "get": { - "description": "get report type list", - "produces": [ - "application/json" - ], - "tags": [ - "Report" - ], - "summary": "get report type list", - "parameters": [ - { - "enum": [ - "question", - "answer", - "comment", - "user" - ], - "type": "string", - "description": "report source", - "name": "source", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/handler.RespBody" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/schema.GetReportTypeResp" - } - } - } - } - ] - } - } - } - } - }, "/answer/api/v1/revisions": { "get": { "description": "get revision list", @@ -6991,35 +6913,6 @@ } } }, - "schema.GetReportTypeResp": { - "type": "object", - "properties": { - "content_type": { - "description": "content type", - "type": "string" - }, - "description": { - "description": "report description", - "type": "string" - }, - "have_content": { - "description": "is have content", - "type": "boolean" - }, - "name": { - "description": "report name", - "type": "string" - }, - "source": { - "description": "report source", - "type": "string" - }, - "type": { - "description": "report type", - "type": "integer" - } - } - }, "schema.GetRevisionResp": { "type": "object", "properties": { @@ -8496,6 +8389,9 @@ "system", "gravatar" ] + }, + "gravatar_base_url": { + "type": "string" } } }, @@ -8529,6 +8425,9 @@ "system", "gravatar" ] + }, + "gravatar_base_url": { + "type": "string" } } }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 8519dc50..7abf1b22 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -693,27 +693,6 @@ definitions: description: url title type: string type: object - schema.GetReportTypeResp: - properties: - content_type: - description: content type - type: string - description: - description: report description - type: string - have_content: - description: is have content - type: boolean - name: - description: report name - type: string - source: - description: report source - type: string - type: - description: report type - type: integer - type: object schema.GetRevisionResp: properties: content: @@ -1752,6 +1731,8 @@ definitions: - system - gravatar type: string + gravatar_base_url: + type: string required: - default_avatar type: object @@ -1774,6 +1755,8 @@ definitions: - system - gravatar type: string + gravatar_base_url: + type: string required: - default_avatar type: object @@ -4451,23 +4434,6 @@ paths: summary: add question and answer tags: - Question - /answer/api/v1/question/closemsglist: - get: - consumes: - - application/json - description: close question msg list - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.RespBody' - security: - - ApiKeyAuth: [] - summary: close question msg list - tags: - - Question /answer/api/v1/question/info: get: consumes: @@ -4781,37 +4747,6 @@ paths: summary: add report tags: - Report - /answer/api/v1/report/type/list: - get: - description: get report type list - parameters: - - description: report source - enum: - - question - - answer - - comment - - user - in: query - name: source - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/handler.RespBody' - - properties: - data: - items: - $ref: '#/definitions/schema.GetReportTypeResp' - type: array - type: object - summary: get report type list - tags: - - Report /answer/api/v1/revisions: get: description: get revision list diff --git a/internal/schema/notification_schema.go b/internal/schema/notification_schema.go index 8688af37..fd9569ca 100644 --- a/internal/schema/notification_schema.go +++ b/internal/schema/notification_schema.go @@ -1,12 +1,16 @@ package schema const ( - NotificationTypeInbox = 1 - NotificationTypeAchievement = 2 - NotificationNotRead = 1 - NotificationRead = 2 - NotificationStatusNormal = 1 - NotificationStatusDelete = 10 + NotificationTypeInbox = 1 + NotificationTypeAchievement = 2 + NotificationNotRead = 1 + NotificationRead = 2 + NotificationStatusNormal = 1 + NotificationStatusDelete = 10 + NotificationInboxTypeAll = 1 + NotificationInboxTypePosts = 2 + NotificationInboxTypeInvites = 3 + NotificationInboxTypeVotes = 4 ) var NotificationType = map[string]int{ @@ -14,6 +18,13 @@ var NotificationType = map[string]int{ "achievement": NotificationTypeAchievement, } +var NotificationInboxType = map[string]int{ + "all": NotificationInboxTypeAll, + "posts": NotificationInboxTypePosts, + "invites": NotificationInboxTypeInvites, + "votes": NotificationInboxTypeVotes, +} + type NotificationContent struct { ID string `json:"id"` TriggerUserID string `json:"-"` //show userid @@ -69,11 +80,13 @@ type RedDot struct { } type NotificationSearch struct { - Page int `json:"page" form:"page"` //Query number of pages - PageSize int `json:"page_size" form:"page_size"` //Search page size - Type int `json:"-" form:"-"` - TypeStr string `json:"type" form:"type"` // inbox achievement - UserID string `json:"-"` + Page int `json:"page" form:"page"` //Query number of pages + PageSize int `json:"page_size" form:"page_size"` //Search page size + Type int `json:"-" form:"-"` + TypeStr string `json:"type" form:"type"` // inbox achievement + InboxTypeStr string `json:"inbox_type" form:"inbox_type"` // inbox achievement + InboxType int `json:"-" form:"-"` // inbox achievement + UserID string `json:"-"` } type NotificationClearRequest struct { diff --git a/internal/service/notification/notification_service.go b/internal/service/notification/notification_service.go index 3a8cce13..3f23be3e 100644 --- a/internal/service/notification/notification_service.go +++ b/internal/service/notification/notification_service.go @@ -123,7 +123,12 @@ func (ns *NotificationService) GetNotificationPage(ctx context.Context, searchCo if !ok { return pager.NewPageModel(0, resp), nil } + searchInboxType, ok := schema.NotificationInboxType[searchCond.InboxTypeStr] + if !ok { + return pager.NewPageModel(0, resp), nil + } searchCond.Type = searchType + searchCond.InboxType = searchInboxType notifications, total, err := ns.notificationRepo.GetNotificationPage(ctx, searchCond) if err != nil { return nil, err From 87d71bf290e8eafb7a06d3fbe8c6d24b17bd56e1 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 24 May 2023 11:04:03 +0800 Subject: [PATCH 13/14] update swagger --- docs/docs.go | 13 +++++++++++++ docs/swagger.json | 13 +++++++++++++ docs/swagger.yaml | 10 ++++++++++ internal/controller/notification_controller.go | 1 + 4 files changed, 37 insertions(+) diff --git a/docs/docs.go b/docs/docs.go index 4a90dfff..2c3d72ea 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2841,6 +2841,19 @@ const docTemplate = `{ "name": "type", "in": "query", "required": true + }, + { + "enum": [ + "all", + "posts", + "invites", + "votes" + ], + "type": "string", + "description": "inbox_type", + "name": "inbox_type", + "in": "query", + "required": true } ], "responses": { diff --git a/docs/swagger.json b/docs/swagger.json index dac3816e..a9cb60df 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2829,6 +2829,19 @@ "name": "type", "in": "query", "required": true + }, + { + "enum": [ + "all", + "posts", + "invites", + "votes" + ], + "type": "string", + "description": "inbox_type", + "name": "inbox_type", + "in": "query", + "required": true } ], "responses": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 7abf1b22..7216dfd2 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -3976,6 +3976,16 @@ paths: name: type required: true type: string + - description: inbox_type + enum: + - all + - posts + - invites + - votes + in: query + name: inbox_type + required: true + type: string produces: - application/json responses: diff --git a/internal/controller/notification_controller.go b/internal/controller/notification_controller.go index f5b88238..7206fead 100644 --- a/internal/controller/notification_controller.go +++ b/internal/controller/notification_controller.go @@ -143,6 +143,7 @@ func (nc *NotificationController) ClearIDUnRead(ctx *gin.Context) { // @Param page query int false "page size" // @Param page_size query int false "page size" // @Param type query string true "type" Enums(inbox,achievement) +// @Param inbox_type query string true "inbox_type" Enums(all,posts,invites,votes) // @Success 200 {object} handler.RespBody // @Router /answer/api/v1/notification/page [get] func (nc *NotificationController) GetList(ctx *gin.Context) { From 0f671dd9fdf019437cb833881d2e4c7fcb6e1e99 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Wed, 24 May 2023 11:16:20 +0800 Subject: [PATCH 14/14] fix(connector): add response data log --- internal/controller/connector_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/controller/connector_controller.go b/internal/controller/connector_controller.go index bb6c37ad..a4ba035b 100644 --- a/internal/controller/connector_controller.go +++ b/internal/controller/connector_controller.go @@ -109,7 +109,7 @@ func (cc *ConnectorController) ConnectorRedirect(connector plugin.Connector) (fn commonRouterPrefix, ConnectorRedirectRouterPrefix, connector.ConnectorSlugName()) userInfo, err := connector.ConnectorReceiver(ctx, receiverURL) if err != nil { - log.Errorf("connector received failed: %v", err) + log.Errorf("connector received failed, error info: %v, response data is: %s", err, userInfo.MetaInfo) ctx.Redirect(http.StatusFound, "/50x") return }