This is an automated email from the ASF dual-hosted git repository. robin0716 pushed a commit to branch develop/robin in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
commit ad1e0aa075cdf1d61b9cdb0cf95fdd0650ae1179 Author: kumfo <ku...@sifou.com> AuthorDate: Thu Aug 8 16:41:17 2024 +0800 feat(badge): badge info && badge award list --- cmd/wire_gen.go | 4 +- docs/docs.go | 156 +++++++++++++++++++++ docs/swagger.json | 156 +++++++++++++++++++++ docs/swagger.yaml | 96 +++++++++++++ i18n/en_US.yaml | 3 + internal/base/reason/reason.go | 1 + internal/controller/badge_controller.go | 61 +++++++- internal/entity/badge_award_entity.go | 8 +- internal/repo/badge/badge_repo.go | 6 + internal/repo/badge_award/badge_award_repo.go | 33 +++-- internal/router/answer_api_router.go | 2 + internal/schema/badge.go | 34 ----- internal/schema/badge_schema.go | 70 +++++++++ internal/service/badge/badge_service.go | 52 ++++++- .../service/badge_award/badge_award_service.go | 102 ++++++++++++-- 15 files changed, 718 insertions(+), 66 deletions(-) diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index c20aa2cf..30b3b65c 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -75,6 +75,7 @@ import ( "github.com/apache/incubator-answer/internal/service/answer_common" auth2 "github.com/apache/incubator-answer/internal/service/auth" badge2 "github.com/apache/incubator-answer/internal/service/badge" + badge_award2 "github.com/apache/incubator-answer/internal/service/badge_award" collection2 "github.com/apache/incubator-answer/internal/service/collection" "github.com/apache/incubator-answer/internal/service/collection_common" comment2 "github.com/apache/incubator-answer/internal/service/comment" @@ -261,7 +262,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, badgeGroupRepo := badge_group.NewBadgeGroupRepo(dataData, uniqueIDRepo) badgeAwardRepo := badge_award.NewBadgeAwardRepo(dataData, uniqueIDRepo) badgeService := badge2.NewBadgeService(badgeRepo, badgeGroupRepo, badgeAwardRepo) - badgeController := controller.NewBadgeController(badgeService) + badgeAwardService := badge_award2.NewBadgeAwardService(badgeAwardRepo, userCommon, objService, questionRepo, answerRepo) + badgeController := controller.NewBadgeController(badgeService, badgeAwardService) answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, userAdminController, reasonController, themeController, siteInfoController, controllerSiteInfoController, notificationController, dashboardController, uploadController, activityController, roleController, pluginCon [...] swaggerRouter := router.NewSwaggerRouter(swaggerConf) uiRouter := router.NewUIRouter(controllerSiteInfoController, siteInfoCommonService) diff --git a/docs/docs.go b/docs/docs.go index 6fc76693..bda5a17b 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2239,6 +2239,117 @@ const docTemplate = `{ } } }, + "/answer/api/v1/badge": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get badge info", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api-badge" + ], + "summary": "get badge info", + "parameters": [ + { + "type": "string", + "default": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.GetBadgeInfoResp" + } + } + } + ] + } + } + } + } + }, + "/answer/api/v1/badge/awards/page": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get badge award list", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api-badge" + ], + "summary": "get badge award list", + "parameters": [ + { + "type": "integer", + "description": "page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "badge id", + "name": "badge_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.GetBadgeInfoResp" + } + } + } + ] + } + } + } + } + }, "/answer/api/v1/badges": { "get": { "security": [ @@ -6942,6 +7053,19 @@ const docTemplate = `{ } } }, + "entity.BadgeLevel": { + "type": "integer", + "enum": [ + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "BadgeLevelBronze", + "BadgeLevelSilver", + "BadgeLevelGold" + ] + }, "handler.RespBody": { "type": "object", "properties": { @@ -7400,6 +7524,9 @@ const docTemplate = `{ "id": { "type": "string" }, + "level": { + "$ref": "#/definitions/entity.BadgeLevel" + }, "name": { "type": "string" } @@ -7656,6 +7783,35 @@ const docTemplate = `{ } } }, + "schema.GetBadgeInfoResp": { + "type": "object", + "properties": { + "award_count": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "earned_count": { + "type": "integer" + }, + "icon": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_single": { + "type": "boolean" + }, + "level": { + "$ref": "#/definitions/entity.BadgeLevel" + }, + "name": { + "type": "string" + } + } + }, "schema.GetBadgeListResp": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 45936363..5ea50e6b 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2209,6 +2209,117 @@ } } }, + "/answer/api/v1/badge": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get badge info", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api-badge" + ], + "summary": "get badge info", + "parameters": [ + { + "type": "string", + "default": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.GetBadgeInfoResp" + } + } + } + ] + } + } + } + } + }, + "/answer/api/v1/badge/awards/page": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get badge award list", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api-badge" + ], + "summary": "get badge award list", + "parameters": [ + { + "type": "integer", + "description": "page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "badge id", + "name": "badge_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.GetBadgeInfoResp" + } + } + } + ] + } + } + } + } + }, "/answer/api/v1/badges": { "get": { "security": [ @@ -6912,6 +7023,19 @@ } } }, + "entity.BadgeLevel": { + "type": "integer", + "enum": [ + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "BadgeLevelBronze", + "BadgeLevelSilver", + "BadgeLevelGold" + ] + }, "handler.RespBody": { "type": "object", "properties": { @@ -7370,6 +7494,9 @@ "id": { "type": "string" }, + "level": { + "$ref": "#/definitions/entity.BadgeLevel" + }, "name": { "type": "string" } @@ -7626,6 +7753,35 @@ } } }, + "schema.GetBadgeInfoResp": { + "type": "object", + "properties": { + "award_count": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "earned_count": { + "type": "integer" + }, + "icon": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_single": { + "type": "boolean" + }, + "level": { + "$ref": "#/definitions/entity.BadgeLevel" + }, + "name": { + "type": "string" + } + } + }, "schema.GetBadgeListResp": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index c91c987e..ca11b20f 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -32,6 +32,16 @@ definitions: minimum: 1 type: integer type: object + entity.BadgeLevel: + enum: + - 1 + - 2 + - 3 + type: integer + x-enum-varnames: + - BadgeLevelBronze + - BadgeLevelSilver + - BadgeLevelGold handler.RespBody: properties: code: @@ -352,6 +362,8 @@ definitions: type: string id: type: string + level: + $ref: '#/definitions/entity.BadgeLevel' name: type: string type: object @@ -526,6 +538,25 @@ definitions: description: if user is followed object will be true,otherwise false type: boolean type: object + schema.GetBadgeInfoResp: + properties: + award_count: + type: integer + description: + type: string + earned_count: + type: integer + icon: + type: string + id: + type: string + is_single: + type: boolean + level: + $ref: '#/definitions/entity.BadgeLevel' + name: + type: string + type: object schema.GetBadgeListResp: properties: badges: @@ -4105,6 +4136,71 @@ paths: summary: recover answer tags: - Answer + /answer/api/v1/badge: + get: + consumes: + - application/json + description: get badge info + parameters: + - default: string + description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/handler.RespBody' + - properties: + data: + $ref: '#/definitions/schema.GetBadgeInfoResp' + type: object + security: + - ApiKeyAuth: [] + summary: get badge info + tags: + - api-badge + /answer/api/v1/badge/awards/page: + get: + consumes: + - application/json + description: get badge award list + parameters: + - description: page + in: query + name: page + type: integer + - description: page size + in: query + name: page_size + type: integer + - description: badge id + in: query + name: badge_id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/handler.RespBody' + - properties: + data: + $ref: '#/definitions/schema.GetBadgeInfoResp' + type: object + security: + - ApiKeyAuth: [] + summary: get badge award list + tags: + - api-badge /answer/api/v1/badges: get: consumes: diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index fa02ed80..1f62e939 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -312,6 +312,9 @@ backend: site_info: config_not_found: other: Site config not found. + badge: + object_not_found: + other: Badge object not found reason: spam: name: diff --git a/internal/base/reason/reason.go b/internal/base/reason/reason.go index 66ef1bed..a6168690 100644 --- a/internal/base/reason/reason.go +++ b/internal/base/reason/reason.go @@ -106,6 +106,7 @@ const ( AddBulkUsersAmountError = "error.user.add_bulk_users_amount_error" InvalidURLError = "error.common.invalid_url" MetaObjectNotFound = "error.meta.object_not_found" + BadgeObjectNotFound = "error.badge.object_not_found" ) // user external login reasons diff --git a/internal/controller/badge_controller.go b/internal/controller/badge_controller.go index ebc534ce..c26d9205 100644 --- a/internal/controller/badge_controller.go +++ b/internal/controller/badge_controller.go @@ -22,17 +22,25 @@ package controller import ( "github.com/apache/incubator-answer/internal/base/handler" "github.com/apache/incubator-answer/internal/base/middleware" + "github.com/apache/incubator-answer/internal/base/pager" + "github.com/apache/incubator-answer/internal/schema" "github.com/apache/incubator-answer/internal/service/badge" + "github.com/apache/incubator-answer/internal/service/badge_award" + "github.com/apache/incubator-answer/pkg/uid" "github.com/gin-gonic/gin" ) type BadgeController struct { - badgeService *badge.BadgeService + badgeService *badge.BadgeService + badgeAwardService *badge_award.BadgeAwardService } -func NewBadgeController(badgeService *badge.BadgeService) *BadgeController { +func NewBadgeController( + badgeService *badge.BadgeService, + badgeAwardService *badge_award.BadgeAwardService) *BadgeController { return &BadgeController{ - badgeService: badgeService, + badgeService: badgeService, + badgeAwardService: badgeAwardService, } } @@ -50,3 +58,50 @@ func (b *BadgeController) GetBadgeList(ctx *gin.Context) { resp, err := b.badgeService.ListByGroup(ctx, userID) handler.HandleResponse(ctx, err, resp) } + +// GetBadgeInfo get badge info +// @Summary get badge info +// @Description get badge info +// @Tags api-badge +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param id query string true "id" default(string) +// @Success 200 {object} handler.RespBody{data=schema.GetBadgeInfoResp} +// @Router /answer/api/v1/badge [get] +func (b *BadgeController) GetBadgeInfo(ctx *gin.Context) { + id := ctx.Query("id") + id = uid.DeShortID(id) + + userID := middleware.GetLoginUserIDFromContext(ctx) + resp, err := b.badgeService.GetBadgeInfo(ctx, id, userID) + handler.HandleResponse(ctx, err, resp) +} + +// GetBadgeAwardList get badge award list +// @Summary get badge award list +// @Description get badge award list +// @Tags api-badge +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param page query int false "page" +// @Param page_size query int false "page size" +// @Param badge_id query string true "badge id" +// @Success 200 {object} handler.RespBody{data=schema.GetBadgeInfoResp} +// @Router /answer/api/v1/badge/awards/page [get] +func (b *BadgeController) GetBadgeAwardList(ctx *gin.Context) { + req := &schema.GetBadgeAwardWithPageReq{} + if handler.BindAndCheck(ctx, req) { + return + } + req.BadgeID = uid.DeShortID(req.BadgeID) + req.UserID = middleware.GetLoginUserIDFromContext(ctx) + + resp, total, err := b.badgeAwardService.GetBadgeAwardList(ctx, req) + if err != nil { + handler.HandleResponse(ctx, err, nil) + return + } + handler.HandleResponse(ctx, nil, pager.NewPageModel(total, resp)) +} diff --git a/internal/entity/badge_award_entity.go b/internal/entity/badge_award_entity.go index a852f6bd..a26fd8de 100644 --- a/internal/entity/badge_award_entity.go +++ b/internal/entity/badge_award_entity.go @@ -26,10 +26,10 @@ type BadgeAward struct { ID string `json:"id" xorm:"id"` CreatedAt time.Time `json:"created_at" xorm:"created not null default CURRENT_TIMESTAMP TIMESTAMP created_at"` UpdatedAt time.Time `json:"updated_at" xorm:"updated not null default CURRENT_TIMESTAMP TIMESTAMP updated_at"` - UserId int64 `json:"user_id" xorm:"not null index BIGINT(20) user_id"` - BadgeId int64 `json:"badge_id" xorm:"not null index BIGINT(20) badge_id"` - ObjectId int64 `json:"object_id" xorm:"not null index BIGINT(20) object_id"` - BadgeGroupId int8 `json:"badge_group_id" xorm:"not null index BIGINT(20) badge_group_id"` + UserID string `json:"user_id" xorm:"not null index BIGINT(20) user_id"` + BadgeID string `json:"badge_id" xorm:"not null index BIGINT(20) badge_id"` + ObjectID string `json:"object_id" xorm:"not null index BIGINT(20) object_id"` + BadgeGroupID int8 `json:"badge_group_id" xorm:"not null index BIGINT(20) badge_group_id"` IsBadgeDeleted int8 `json:"is_badge_deleted" xorm:"not null index TINYINT(1) s_badge_deleted"` } diff --git a/internal/repo/badge/badge_repo.go b/internal/repo/badge/badge_repo.go index b4cd72f1..7651f95b 100644 --- a/internal/repo/badge/badge_repo.go +++ b/internal/repo/badge/badge_repo.go @@ -40,6 +40,12 @@ func NewBadgeRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo) badge.Badge } } +func (r badgeRepo) GetByID(ctx context.Context, id string) (badge *entity.Badge, exists bool, err error) { + badge = &entity.Badge{} + exists, err = r.data.DB.Context(ctx).Where("id = ?", id).Get(badge) + return +} + // ListByLevel returns a list of badges by level func (r *badgeRepo) ListByLevel(ctx context.Context, level entity.BadgeLevel) (badges []*entity.Badge, err error) { badges = make([]*entity.Badge, 0) diff --git a/internal/repo/badge_award/badge_award_repo.go b/internal/repo/badge_award/badge_award_repo.go index b6f0e95e..0a460347 100644 --- a/internal/repo/badge_award/badge_award_repo.go +++ b/internal/repo/badge_award/badge_award_repo.go @@ -22,9 +22,12 @@ package badge_award import ( "context" "github.com/apache/incubator-answer/internal/base/data" + "github.com/apache/incubator-answer/internal/base/pager" + "github.com/apache/incubator-answer/internal/base/reason" "github.com/apache/incubator-answer/internal/entity" "github.com/apache/incubator-answer/internal/service/badge_award" "github.com/apache/incubator-answer/internal/service/unique" + "github.com/segmentfault/pacman/errors" "time" ) @@ -53,6 +56,10 @@ func (r *badgeAwardRepo) CountByUserId(ctx context.Context, userID string) (awar return } func (r *badgeAwardRepo) CountByUserIdAndBadgeId(ctx context.Context, userID string, badgeID string) (awardCount int64) { + awardCount, err := r.data.DB.Context(ctx).Where("user_id = ? AND badge_id = ?", userID, badgeID).Count(&entity.BadgeAward{}) + if err != nil { + return 0 + } return } func (r *badgeAwardRepo) CountByObjectId(ctx context.Context, objectID string) (awardCount int64) { @@ -71,31 +78,39 @@ func (r *badgeAwardRepo) SumUserEarnedGroupByBadgeID(ctx context.Context, userID func (r *badgeAwardRepo) ListAllByUserId(ctx context.Context, userID string) (badgeAwards []*entity.BadgeAward) { return } -func (r *badgeAwardRepo) ListPagedByBadgeId(ctx context.Context, badgeID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) { + +// ListPagedByBadgeId list badge awards by badge id +func (r *badgeAwardRepo) ListPagedByBadgeId(ctx context.Context, badgeID string, page int, pageSize int) (badgeAwardList []*entity.BadgeAward, total int64, err error) { + session := r.data.DB.Context(ctx) + session.Where("badge_id = ?", badgeID) + total, err = pager.Help(page, pageSize, &badgeAwardList, &entity.Question{}, session) + if err != nil { + err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } return } -func (r *badgeAwardRepo) ListPagedByBadgeIdAndUserId(ctx context.Context, badgeID string, userID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) { +func (r *badgeAwardRepo) ListPagedByBadgeIdAndUserId(ctx context.Context, badgeID string, userID string, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) { return } -func (r *badgeAwardRepo) ListPagedByObjectId(ctx context.Context, badgeID string, objectID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) { +func (r *badgeAwardRepo) ListPagedByObjectId(ctx context.Context, badgeID string, objectID string, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) { return } -func (r *badgeAwardRepo) ListPagedByObjectIdAndUserId(ctx context.Context, badgeID string, objectID string, userID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) { +func (r *badgeAwardRepo) ListPagedByObjectIdAndUserId(ctx context.Context, badgeID string, objectID string, userID string, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) { return } -func (r *badgeAwardRepo) ListTagPagedByBadgeId(ctx context.Context, badgeIDs []int64, page int64, pageSize int64, filterUserID int64) (badgeAwards []*entity.BadgeAward, total int64) { +func (r *badgeAwardRepo) ListTagPagedByBadgeId(ctx context.Context, badgeIDs []string, page int, pageSize int, filterUserID string) (badgeAwards []*entity.BadgeAward, total int64, err error) { return } -func (r *badgeAwardRepo) ListTagPagedByBadgeIdAndUserId(ctx context.Context, badgeIDs []int64, userID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) { +func (r *badgeAwardRepo) ListTagPagedByBadgeIdAndUserId(ctx context.Context, badgeIDs []string, userID string, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) { return } -func (r *badgeAwardRepo) ListPagedLatest(ctx context.Context, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) { +func (r *badgeAwardRepo) ListPagedLatest(ctx context.Context, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) { return } -func (r *badgeAwardRepo) ListNewestEarnedByLevel(ctx context.Context, userID string, level entity.BadgeLevel, num int64) (badgeAwards []*entity.BadgeAward, total int64) { +func (r *badgeAwardRepo) ListNewestEarnedByLevel(ctx context.Context, userID string, level entity.BadgeLevel, num int) (badgeAwards []*entity.BadgeAward, total int64, err error) { return } -func (r *badgeAwardRepo) ListNewestByUserIdAndLevel(ctx context.Context, userID string, level int64, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) { +func (r *badgeAwardRepo) ListNewestByUserIdAndLevel(ctx context.Context, userID string, level int, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) { return } func (r *badgeAwardRepo) GetByUserIdAndBadgeId(ctx context.Context, userID string, badgeID string) (badgeAward *entity.BadgeAward) { diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index dadf2a85..c7d4c38e 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -192,6 +192,8 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) { r.GET("/meta/reaction", a.metaController.GetReaction) // badges + r.GET("/badge", a.badgeController.GetBadgeInfo) + r.GET("/badge/awards/page", a.badgeController.GetBadgeAwardList) r.GET("/badges", a.badgeController.GetBadgeList) } diff --git a/internal/schema/badge.go b/internal/schema/badge.go deleted file mode 100644 index c52a235b..00000000 --- a/internal/schema/badge.go +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package schema - -// BadgeListInfo get badge list response -type BadgeListInfo struct { - ID string `json:"id" ` - Name string `json:"name" ` - Icon string `json:"icon" ` - AwardCount int `json:"award_count" ` - Earned bool `json:"earned" ` -} - -type GetBadgeListResp struct { - Badges []*BadgeListInfo `json:"badges" ` - GroupName string `json:"group_name" ` -} diff --git a/internal/schema/badge_schema.go b/internal/schema/badge_schema.go new file mode 100644 index 00000000..06d92ecc --- /dev/null +++ b/internal/schema/badge_schema.go @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package schema + +import "github.com/apache/incubator-answer/internal/entity" + +// BadgeListInfo get badge list response +type BadgeListInfo struct { + ID string `json:"id" ` + Name string `json:"name" ` + Icon string `json:"icon" ` + AwardCount int `json:"award_count" ` + Earned bool `json:"earned" ` + Level entity.BadgeLevel `json:"level" ` +} + +type GetBadgeListResp struct { + Badges []*BadgeListInfo `json:"badges" ` + GroupName string `json:"group_name" ` +} + +type GetBadgeInfoResp struct { + ID string `json:"id" ` + Name string `json:"name" ` + Description string `json:"description" ` + Icon string `json:"icon" ` + AwardCount int `json:"award_count" ` + EarnedCount int64 `json:"earned_count" ` + IsSingle bool `json:"is_single" ` + Level entity.BadgeLevel `json:"level" ` +} + +type GetBadgeAwardWithPageReq struct { + // page + Page int `validate:"omitempty,min=1" form:"page"` + // page size + PageSize int `validate:"omitempty,min=1" form:"page_size"` + // badge id + BadgeID string `validate:"required" form:"badge_id"` + // user id + UserID string `json:"-"` +} + +type GetBadgeAwardWithPageResp struct { + CreatedAt int64 `json:"created_at"` + ObjectID string `json:"object_id"` + QuestionID string `json:"question_id"` + AnswerID string `json:"answer_id"` + CommentID string `json:"comment_id"` + ObjectType string `json:"object_type" enums:"question,answer,comment"` + UrlTitle string `json:"url_title"` + AuthorUserInfo UserBasicInfo `json:"author_user_info"` +} diff --git a/internal/service/badge/badge_service.go b/internal/service/badge/badge_service.go index 1109b444..94ba5b64 100644 --- a/internal/service/badge/badge_service.go +++ b/internal/service/badge/badge_service.go @@ -22,15 +22,20 @@ package badge import ( "context" "github.com/apache/incubator-answer/internal/base/handler" + "github.com/apache/incubator-answer/internal/base/reason" "github.com/apache/incubator-answer/internal/base/translator" "github.com/apache/incubator-answer/internal/entity" "github.com/apache/incubator-answer/internal/schema" "github.com/apache/incubator-answer/internal/service/badge_award" "github.com/apache/incubator-answer/internal/service/badge_group" "github.com/apache/incubator-answer/pkg/converter" + "github.com/apache/incubator-answer/pkg/uid" + "github.com/gin-gonic/gin" + "github.com/segmentfault/pacman/errors" ) type BadgeRepo interface { + GetByID(ctx context.Context, id string) (badge *entity.Badge, exists bool, err error) ListByLevel(ctx context.Context, level entity.BadgeLevel) ([]*entity.Badge, error) ListByGroup(ctx context.Context, groupID int64) ([]*entity.Badge, error) ListByLevelAndGroup(ctx context.Context, level entity.BadgeLevel, groupID int64) ([]*entity.Badge, error) @@ -55,6 +60,7 @@ func NewBadgeService( } } +// ListByGroup list all badges group by group func (b *BadgeService) ListByGroup(ctx context.Context, userID string) (resp []*schema.GetBadgeListResp, err error) { var ( groups []*entity.BadgeGroup @@ -74,7 +80,13 @@ func (b *BadgeService) ListByGroup(ctx context.Context, userID string) (resp []* if err != nil { return } - earnedCounts, err = b.badgeAwardRepo.SumUserEarnedGroupByBadgeID(ctx, userID) + + if len(userID) > 0 { + earnedCounts, err = b.badgeAwardRepo.SumUserEarnedGroupByBadgeID(ctx, userID) + if err != nil { + return + } + } for _, group := range groups { groupMap[converter.StringToInt64(group.ID)] = group.Name @@ -93,11 +105,12 @@ func (b *BadgeService) ListByGroup(ctx context.Context, userID string) (resp []* } badgesMap[badge.BadgeGroupId] = append(badgesMap[badge.BadgeGroupId], &schema.BadgeListInfo{ - ID: badge.ID, + ID: uid.EnShortID(badge.ID), Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name), Icon: badge.Icon, AwardCount: badge.AwardCount, Earned: earned, + Level: badge.Level, }) } @@ -110,3 +123,38 @@ func (b *BadgeService) ListByGroup(ctx context.Context, userID string) (resp []* return } + +// GetBadgeInfo get badge info +func (b *BadgeService) GetBadgeInfo(ctx *gin.Context, id string, userID string) (info *schema.GetBadgeInfoResp, err error) { + var ( + badge *entity.Badge + earnedTotal int64 = 0 + exists = false + ) + + badge, exists, err = b.badgeRepo.GetByID(ctx, id) + if err != nil { + return + } + + if !exists || badge.Status == entity.BadgeStatusInactive { + err = errors.BadRequest(reason.BadgeObjectNotFound) + return + } + + if len(userID) > 0 { + earnedTotal = b.badgeAwardRepo.CountByUserIdAndBadgeId(ctx, userID, badge.ID) + } + + info = &schema.GetBadgeInfoResp{ + ID: uid.EnShortID(badge.ID), + Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name), + Description: translator.Tr(handler.GetLangByCtx(ctx), badge.Description), + Icon: badge.Icon, + AwardCount: badge.AwardCount, + EarnedCount: earnedTotal, + IsSingle: badge.Single == entity.BadgeSingleAward, + Level: badge.Level, + } + return +} diff --git a/internal/service/badge_award/badge_award_service.go b/internal/service/badge_award/badge_award_service.go index 8f84453e..b8e96545 100644 --- a/internal/service/badge_award/badge_award_service.go +++ b/internal/service/badge_award/badge_award_service.go @@ -21,7 +21,17 @@ package badge_award import ( "context" + "github.com/apache/incubator-answer/internal/base/reason" "github.com/apache/incubator-answer/internal/entity" + "github.com/apache/incubator-answer/internal/schema" + answercommon "github.com/apache/incubator-answer/internal/service/answer_common" + "github.com/apache/incubator-answer/internal/service/object_info" + questioncommon "github.com/apache/incubator-answer/internal/service/question_common" + usercommon "github.com/apache/incubator-answer/internal/service/user_common" + "github.com/apache/incubator-answer/pkg/htmltext" + "github.com/jinzhu/copier" + "github.com/segmentfault/pacman/errors" + "github.com/segmentfault/pacman/log" "time" ) @@ -33,32 +43,98 @@ type BadgeAwardRepo interface { CountByUserId(ctx context.Context, userID string) (awardCount int64) CountByUserIdAndBadgeId(ctx context.Context, userID string, badgeID string) (awardCount int64) CountByObjectId(ctx context.Context, objectID string) (awardCount int64) - CountByObjectIdAndBadgeId(ctx context.Context, objectID string, badgeID string) int64 + CountByObjectIdAndBadgeId(ctx context.Context, objectID string, badgeID string) (awardCount int64) CountBadgesByUserIdAndObjectId(ctx context.Context, userID string, objectID string, badgeID string) (awardCount int64) SumUserEarnedGroupByBadgeID(ctx context.Context, userID string) (earnedCounts []*entity.BadgeEarnedCount, err error) ListAllByUserId(ctx context.Context, userID string) (badgeAwards []*entity.BadgeAward) - ListPagedByBadgeId(ctx context.Context, badgeID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) - ListPagedByBadgeIdAndUserId(ctx context.Context, badgeID string, userID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) - ListPagedByObjectId(ctx context.Context, badgeID string, objectID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) - ListPagedByObjectIdAndUserId(ctx context.Context, badgeID string, objectID string, userID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) - ListTagPagedByBadgeId(ctx context.Context, badgeIDs []int64, page int64, pageSize int64, filterUserID int64) (badgeAwards []*entity.BadgeAward, total int64) - ListTagPagedByBadgeIdAndUserId(ctx context.Context, badgeIDs []int64, userID string, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) - ListPagedLatest(ctx context.Context, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) - ListNewestEarnedByLevel(ctx context.Context, userID string, level entity.BadgeLevel, num int64) (badgeAwards []*entity.BadgeAward, total int64) - ListNewestByUserIdAndLevel(ctx context.Context, userID string, level int64, page int64, pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) + ListPagedByBadgeId(ctx context.Context, badgeID string, page int, pageSize int) (badgeAwardList []*entity.BadgeAward, total int64, err error) + ListPagedByBadgeIdAndUserId(ctx context.Context, badgeID string, userID string, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) + ListPagedByObjectId(ctx context.Context, badgeID string, objectID string, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) + ListPagedByObjectIdAndUserId(ctx context.Context, badgeID string, objectID string, userID string, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) + ListTagPagedByBadgeId(ctx context.Context, badgeIDs []string, page int, pageSize int, filterUserID string) (badgeAwards []*entity.BadgeAward, total int64, err error) + ListTagPagedByBadgeIdAndUserId(ctx context.Context, badgeIDs []string, userID string, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) + ListPagedLatest(ctx context.Context, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) + ListNewestEarnedByLevel(ctx context.Context, userID string, level entity.BadgeLevel, num int) (badgeAwards []*entity.BadgeAward, total int64, err error) + ListNewestByUserIdAndLevel(ctx context.Context, userID string, level int, page int, pageSize int) (badgeAwards []*entity.BadgeAward, total int64, err error) GetByUserIdAndBadgeId(ctx context.Context, userID string, badgeID string) (badgeAward *entity.BadgeAward) GetByUserIdAndBadgeIdAndObjectId(ctx context.Context, userID string, badgeID string, objectID string) (badgeAward *entity.BadgeAward) } type BadgeAwardService struct { - badgeAwardRepo BadgeAwardRepo + badgeAwardRepo BadgeAwardRepo + userCommon *usercommon.UserCommon + objectInfoService *object_info.ObjService + questionRepo questioncommon.QuestionRepo + answerRepo answercommon.AnswerRepo } -func NewBadgeAwardService(badgeAwardRepo BadgeAwardRepo) *BadgeAwardService { +func NewBadgeAwardService( + badgeAwardRepo BadgeAwardRepo, + userCommon *usercommon.UserCommon, + objectInfoService *object_info.ObjService, + questionRepo questioncommon.QuestionRepo, + answerRepo answercommon.AnswerRepo, +) *BadgeAwardService { return &BadgeAwardService{ - badgeAwardRepo: badgeAwardRepo, + badgeAwardRepo: badgeAwardRepo, + userCommon: userCommon, + objectInfoService: objectInfoService, + questionRepo: questionRepo, + answerRepo: answerRepo, } } + +// GetBadgeAwardList get badge award list +func (b *BadgeAwardService) GetBadgeAwardList( + ctx context.Context, req *schema.GetBadgeAwardWithPageReq, +) (resp []*schema.GetBadgeAwardWithPageResp, total int64, err error) { + var ( + badgeAwardList []*entity.BadgeAward + ) + + badgeAwardList, total, err = b.badgeAwardRepo.ListPagedByBadgeId(ctx, req.BadgeID, req.Page, req.PageSize) + if err != nil { + return + } + + resp = make([]*schema.GetBadgeAwardWithPageResp, 0, len(badgeAwardList)) + + for i, badgeAward := range badgeAwardList { + objInfo, e := b.objectInfoService.GetInfo(ctx, badgeAward.ObjectID) + if e != nil { + err = e + return + } + if objInfo.IsDeleted() { + err = errors.BadRequest(reason.NewObjectAlreadyDeleted) + return + } + + row := &schema.GetBadgeAwardWithPageResp{ + CreatedAt: badgeAward.CreatedAt.Unix(), + ObjectID: badgeAward.ObjectID, + QuestionID: objInfo.QuestionID, + AnswerID: objInfo.AnswerID, + CommentID: objInfo.CommentID, + ObjectType: objInfo.ObjectType, + UrlTitle: htmltext.UrlTitle(objInfo.Title), + AuthorUserInfo: schema.UserBasicInfo{}, + } + + // get user info + userInfo, exists, e := b.userCommon.GetUserBasicInfoByID(ctx, badgeAward.UserID) + if e != nil { + log.Errorf("user not found by id: %s, err: %v", badgeAward.UserID, e) + } + if exists { + _ = copier.Copy(&row.AuthorUserInfo, userInfo) + } + + resp[i] = row + } + + return +}