This is an automated email from the ASF dual-hosted git repository.
kumfo pushed a commit to branch feat/1.4.0/badge
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
The following commit(s) were added to refs/heads/feat/1.4.0/badge by this push:
new bb40366f feat(badge): badge manage and user center's badge list
bb40366f is described below
commit bb40366f001fa183387a4d2a6d2676d866cb218e
Author: kumfo <[email protected]>
AuthorDate: Tue Aug 13 16:12:34 2024 +0800
feat(badge): badge manage and user center's badge list
---
cmd/wire_gen.go | 3 +-
docs/docs.go | 188 ++++++++++++++++++++++++++
docs/swagger.json | 188 ++++++++++++++++++++++++++
docs/swagger.yaml | 118 ++++++++++++++++
i18n/en_US.yaml | 12 ++
internal/base/reason/reason.go | 1 +
internal/controller_admin/badge_controller.go | 85 ++++++++++++
internal/controller_admin/controller.go | 1 +
internal/entity/badge_award_entity.go | 2 +-
internal/entity/badge_entity.go | 2 +-
internal/migrations/v22.go | 6 +-
internal/repo/badge/badge_repo.go | 62 ++++++++-
internal/router/answer_api_router.go | 7 +
internal/schema/badge_schema.go | 54 ++++++++
internal/service/badge/badge_service.go | 93 ++++++++++++-
15 files changed, 806 insertions(+), 16 deletions(-)
diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go
index 309af935..d4034b6b 100644
--- a/cmd/wire_gen.go
+++ b/cmd/wire_gen.go
@@ -267,7 +267,8 @@ func initApplication(debug bool, serverConf *conf.Server,
dbConf *data.Database,
badgeService := badge2.NewBadgeService(badgeRepo, badgeGroupRepo,
badgeAwardRepo, badgeEventService)
badgeAwardService := badge2.NewBadgeAwardService(badgeAwardRepo,
badgeRepo, userCommon, objService)
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 [...]
+ controller_adminBadgeController :=
controller_admin.NewBadgeController(badgeService)
+ 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)
authUserMiddleware := middleware.NewAuthUserMiddleware(authService,
siteInfoCommonService)
diff --git a/docs/docs.go b/docs/docs.go
index ecc62169..eaa7133a 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -154,6 +154,113 @@ const docTemplate = `{
}
}
},
+ "/answer/admin/api/badge/status": {
+ "put": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "update badge status",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "AdminBadge"
+ ],
+ "summary": "update badge status",
+ "parameters": [
+ {
+ "description": "UpdateBadgeStatusReq",
+ "name": "data",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/schema.UpdateBadgeStatusReq"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/handler.RespBody"
+ }
+ }
+ }
+ }
+ },
+ "/answer/admin/api/badges": {
+ "get": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "list all badges by page",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "AdminBadge"
+ ],
+ "summary": "list all badges by page",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "page",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size",
+ "name": "page_size",
+ "in": "query"
+ },
+ {
+ "enum": [
+ "",
+ "active",
+ "inactive"
+ ],
+ "type": "string",
+ "description": "badge status",
+ "name": "status",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/handler.RespBody"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref":
"#/definitions/schema.GetBadgeListPagedResp"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
"/answer/admin/api/dashboard": {
"get": {
"security": [
@@ -7652,6 +7759,17 @@ const docTemplate = `{
}
}
},
+ "schema.BadgeStatus": {
+ "type": "string",
+ "enum": [
+ "active",
+ "inactive"
+ ],
+ "x-enum-varnames": [
+ "BadgeStatusActive",
+ "BadgeStatusInactive"
+ ]
+ },
"schema.CloseQuestionReq": {
"type": "object",
"required": [
@@ -7944,6 +8062,55 @@ const docTemplate = `{
}
}
},
+ "schema.GetBadgeListPagedResp": {
+ "type": "object",
+ "properties": {
+ "award_count": {
+ "description": "badge award count",
+ "type": "integer"
+ },
+ "description": {
+ "description": "badge description",
+ "type": "string"
+ },
+ "earned": {
+ "description": "badge earned count",
+ "type": "boolean"
+ },
+ "group_name": {
+ "description": "badge group name",
+ "type": "string"
+ },
+ "icon": {
+ "description": "badge icon",
+ "type": "string"
+ },
+ "id": {
+ "description": "badge id",
+ "type": "string"
+ },
+ "level": {
+ "description": "badge level",
+ "allOf": [
+ {
+ "$ref": "#/definitions/entity.BadgeLevel"
+ }
+ ]
+ },
+ "name": {
+ "description": "badge name",
+ "type": "string"
+ },
+ "status": {
+ "description": "badge status",
+ "allOf": [
+ {
+ "$ref": "#/definitions/schema.BadgeStatus"
+ }
+ ]
+ }
+ }
+ },
"schema.GetBadgeListResp": {
"type": "object",
"properties": {
@@ -10446,6 +10613,27 @@ const docTemplate = `{
}
}
},
+ "schema.UpdateBadgeStatusReq": {
+ "type": "object",
+ "required": [
+ "id",
+ "status"
+ ],
+ "properties": {
+ "id": {
+ "description": "badge id",
+ "type": "string"
+ },
+ "status": {
+ "description": "badge status",
+ "allOf": [
+ {
+ "$ref": "#/definitions/schema.BadgeStatus"
+ }
+ ]
+ }
+ }
+ },
"schema.UpdateCommentReq": {
"type": "object",
"required": [
diff --git a/docs/swagger.json b/docs/swagger.json
index 1412f179..116a3a6a 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -124,6 +124,113 @@
}
}
},
+ "/answer/admin/api/badge/status": {
+ "put": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "update badge status",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "AdminBadge"
+ ],
+ "summary": "update badge status",
+ "parameters": [
+ {
+ "description": "UpdateBadgeStatusReq",
+ "name": "data",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/schema.UpdateBadgeStatusReq"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/handler.RespBody"
+ }
+ }
+ }
+ }
+ },
+ "/answer/admin/api/badges": {
+ "get": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "list all badges by page",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "AdminBadge"
+ ],
+ "summary": "list all badges by page",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "page",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size",
+ "name": "page_size",
+ "in": "query"
+ },
+ {
+ "enum": [
+ "",
+ "active",
+ "inactive"
+ ],
+ "type": "string",
+ "description": "badge status",
+ "name": "status",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/handler.RespBody"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref":
"#/definitions/schema.GetBadgeListPagedResp"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
"/answer/admin/api/dashboard": {
"get": {
"security": [
@@ -7622,6 +7729,17 @@
}
}
},
+ "schema.BadgeStatus": {
+ "type": "string",
+ "enum": [
+ "active",
+ "inactive"
+ ],
+ "x-enum-varnames": [
+ "BadgeStatusActive",
+ "BadgeStatusInactive"
+ ]
+ },
"schema.CloseQuestionReq": {
"type": "object",
"required": [
@@ -7914,6 +8032,55 @@
}
}
},
+ "schema.GetBadgeListPagedResp": {
+ "type": "object",
+ "properties": {
+ "award_count": {
+ "description": "badge award count",
+ "type": "integer"
+ },
+ "description": {
+ "description": "badge description",
+ "type": "string"
+ },
+ "earned": {
+ "description": "badge earned count",
+ "type": "boolean"
+ },
+ "group_name": {
+ "description": "badge group name",
+ "type": "string"
+ },
+ "icon": {
+ "description": "badge icon",
+ "type": "string"
+ },
+ "id": {
+ "description": "badge id",
+ "type": "string"
+ },
+ "level": {
+ "description": "badge level",
+ "allOf": [
+ {
+ "$ref": "#/definitions/entity.BadgeLevel"
+ }
+ ]
+ },
+ "name": {
+ "description": "badge name",
+ "type": "string"
+ },
+ "status": {
+ "description": "badge status",
+ "allOf": [
+ {
+ "$ref": "#/definitions/schema.BadgeStatus"
+ }
+ ]
+ }
+ }
+ },
"schema.GetBadgeListResp": {
"type": "object",
"properties": {
@@ -10416,6 +10583,27 @@
}
}
},
+ "schema.UpdateBadgeStatusReq": {
+ "type": "object",
+ "required": [
+ "id",
+ "status"
+ ],
+ "properties": {
+ "id": {
+ "description": "badge id",
+ "type": "string"
+ },
+ "status": {
+ "description": "badge status",
+ "allOf": [
+ {
+ "$ref": "#/definitions/schema.BadgeStatus"
+ }
+ ]
+ }
+ }
+ },
"schema.UpdateCommentReq": {
"type": "object",
"required": [
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index e2fc6682..116b5932 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -374,6 +374,14 @@ definitions:
description: badge name
type: string
type: object
+ schema.BadgeStatus:
+ enum:
+ - active
+ - inactive
+ type: string
+ x-enum-varnames:
+ - BadgeStatusActive
+ - BadgeStatusInactive
schema.CloseQuestionReq:
properties:
close_msg:
@@ -573,6 +581,38 @@ definitions:
description: badge name
type: string
type: object
+ schema.GetBadgeListPagedResp:
+ properties:
+ award_count:
+ description: badge award count
+ type: integer
+ description:
+ description: badge description
+ type: string
+ earned:
+ description: badge earned count
+ type: boolean
+ group_name:
+ description: badge group name
+ type: string
+ icon:
+ description: badge icon
+ type: string
+ id:
+ description: badge id
+ type: string
+ level:
+ allOf:
+ - $ref: '#/definitions/entity.BadgeLevel'
+ description: badge level
+ name:
+ description: badge name
+ type: string
+ status:
+ allOf:
+ - $ref: '#/definitions/schema.BadgeStatus'
+ description: badge status
+ type: object
schema.GetBadgeListResp:
properties:
badges:
@@ -2307,6 +2347,19 @@ definitions:
url_title:
type: string
type: object
+ schema.UpdateBadgeStatusReq:
+ properties:
+ id:
+ description: badge id
+ type: string
+ status:
+ allOf:
+ - $ref: '#/definitions/schema.BadgeStatus'
+ description: badge status
+ required:
+ - id
+ - status
+ type: object
schema.UpdateCommentReq:
properties:
captcha_code:
@@ -2924,6 +2977,71 @@ paths:
summary: update answer status
tags:
- admin
+ /answer/admin/api/badge/status:
+ put:
+ consumes:
+ - application/json
+ description: update badge status
+ parameters:
+ - description: UpdateBadgeStatusReq
+ in: body
+ name: data
+ required: true
+ schema:
+ $ref: '#/definitions/schema.UpdateBadgeStatusReq'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/handler.RespBody'
+ security:
+ - ApiKeyAuth: []
+ summary: update badge status
+ tags:
+ - AdminBadge
+ /answer/admin/api/badges:
+ get:
+ consumes:
+ - application/json
+ description: list all badges by page
+ parameters:
+ - description: page
+ in: query
+ name: page
+ type: integer
+ - description: page size
+ in: query
+ name: page_size
+ type: integer
+ - description: badge status
+ enum:
+ - ""
+ - active
+ - inactive
+ in: query
+ name: status
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ allOf:
+ - $ref: '#/definitions/handler.RespBody'
+ - properties:
+ data:
+ items:
+ $ref: '#/definitions/schema.GetBadgeListPagedResp'
+ type: array
+ type: object
+ security:
+ - ApiKeyAuth: []
+ summary: list all badges by page
+ tags:
+ - AdminBadge
/answer/admin/api/dashboard:
get:
consumes:
diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 0d7f2ecf..071644f4 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -146,6 +146,8 @@ backend:
common:
invalid_url:
other: Invalid URL.
+ status_invalid:
+ other: Invalid status.
password:
space_invalid:
other: Password cannot contain spaces.
@@ -776,6 +778,16 @@ backend:
other: Famous Link
desc:
other: Posted an external link with 100 clicks.
+ default_badge_groups:
+ getting_started:
+ name:
+ other: Getting Started
+ community:
+ name:
+ other: Community
+ posting:
+ name:
+ other: Posting
# The following fields are used for interface presentation(Front-end)
ui:
diff --git a/internal/base/reason/reason.go b/internal/base/reason/reason.go
index a6168690..24d7ab5f 100644
--- a/internal/base/reason/reason.go
+++ b/internal/base/reason/reason.go
@@ -107,6 +107,7 @@ const (
InvalidURLError = "error.common.invalid_url"
MetaObjectNotFound = "error.meta.object_not_found"
BadgeObjectNotFound = "error.badge.object_not_found"
+ StatusInvalid = "error.common.status_invalid"
)
// user external login reasons
diff --git a/internal/controller_admin/badge_controller.go
b/internal/controller_admin/badge_controller.go
new file mode 100644
index 00000000..8842592f
--- /dev/null
+++ b/internal/controller_admin/badge_controller.go
@@ -0,0 +1,85 @@
+/*
+ * 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 controller_admin
+
+import (
+ "github.com/apache/incubator-answer/internal/base/handler"
+ "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/gin-gonic/gin"
+)
+
+type BadgeController struct {
+ badgeService *badge.BadgeService
+}
+
+func NewBadgeController(badgeService *badge.BadgeService) *BadgeController {
+ return &BadgeController{
+ badgeService: badgeService,
+ }
+}
+
+// GetBadgeList list all badges by page
+// @Summary list all badges by page
+// @Description list all badges by page
+// @Tags AdminBadge
+// @Accept json
+// @Produce json
+// @Security ApiKeyAuth
+// @Param page query int false "page"
+// @Param page_size query int false "page size"
+// @Param status query string false "badge status" Enums(, active, inactive)
+// @Success 200 {object} handler.RespBody{data=[]schema.GetBadgeListPagedResp}
+// @Router /answer/admin/api/badges [get]
+func (b *BadgeController) GetBadgeList(ctx *gin.Context) {
+ req := &schema.GetBadgeListPagedReq{}
+ if handler.BindAndCheck(ctx, req) {
+ return
+ }
+
+ resp, total, err := b.badgeService.ListPaged(ctx, req)
+ if err != nil {
+ handler.HandleResponse(ctx, err, nil)
+ return
+ }
+
+ handler.HandleResponse(ctx, nil, pager.NewPageModel(total, resp))
+}
+
+// UpdateBadgeStatus update badge status
+// @Summary update badge status
+// @Description update badge status
+// @Tags AdminBadge
+// @Accept json
+// @Produce json
+// @Security ApiKeyAuth
+// @Param data body schema.UpdateBadgeStatusReq true "UpdateBadgeStatusReq"
+// @Success 200 {object} handler.RespBody
+// @Router /answer/admin/api/badge/status [put]
+func (b *BadgeController) UpdateBadgeStatus(ctx *gin.Context) {
+ req := &schema.UpdateBadgeStatusReq{}
+ if handler.BindAndCheck(ctx, req) {
+ return
+ }
+
+ err := b.badgeService.UpdateStatus(ctx, req)
+ handler.HandleResponse(ctx, err, nil)
+}
diff --git a/internal/controller_admin/controller.go
b/internal/controller_admin/controller.go
index de87d105..ebf32cbf 100644
--- a/internal/controller_admin/controller.go
+++ b/internal/controller_admin/controller.go
@@ -28,4 +28,5 @@ var ProviderSetController = wire.NewSet(
NewSiteInfoController,
NewRoleController,
NewPluginController,
+ NewBadgeController,
)
diff --git a/internal/entity/badge_award_entity.go
b/internal/entity/badge_award_entity.go
index 1d421623..0eb30227 100644
--- a/internal/entity/badge_award_entity.go
+++ b/internal/entity/badge_award_entity.go
@@ -28,7 +28,7 @@ const (
// BadgeAward badge_award
type BadgeAward struct {
- ID string `json:"id" xorm:"id"`
+ ID string `xorm:"not null pk BIGINT(20) 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 string `json:"user_id" xorm:"not null index
BIGINT(20) user_id"`
diff --git a/internal/entity/badge_entity.go b/internal/entity/badge_entity.go
index 96babcb9..5177c93c 100644
--- a/internal/entity/badge_entity.go
+++ b/internal/entity/badge_entity.go
@@ -41,7 +41,7 @@ const (
// Badge badge
type Badge struct {
- ID string `xorm:"not null pk autoincr BIGINT(20) id"`
+ ID string `xorm:"not null pk BIGINT(20) id"`
CreatedAt time.Time `xorm:"created not null default
CURRENT_TIMESTAMP TIMESTAMP created_at"`
UpdatedAt time.Time `xorm:"updated not null default
CURRENT_TIMESTAMP TIMESTAMP updated_at"`
Name string `xorm:"not null default '' VARCHAR(256) name"`
diff --git a/internal/migrations/v22.go b/internal/migrations/v22.go
index f5793d4a..d3d81d84 100644
--- a/internal/migrations/v22.go
+++ b/internal/migrations/v22.go
@@ -30,9 +30,9 @@ import (
var (
defaultBadgeGroupTable = []*entity.BadgeGroup{
- {ID: "1", Name: "Getting Started"},
- {ID: "2", Name: "Community"},
- {ID: "3", Name: "Posting"},
+ {ID: "1", Name:
"badge.default_badge_groups.getting_started.name"},
+ {ID: "2", Name: "badge.default_badge_groups.community.name"},
+ {ID: "3", Name: "badge.default_badge_groups.posting.name"},
}
defaultBadgeTable = []*entity.Badge{
diff --git a/internal/repo/badge/badge_repo.go
b/internal/repo/badge/badge_repo.go
index d4d2f992..ebdb0425 100644
--- a/internal/repo/badge/badge_repo.go
+++ b/internal/repo/badge/badge_repo.go
@@ -22,11 +22,13 @@ package badge
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"
"github.com/apache/incubator-answer/internal/service/unique"
"github.com/segmentfault/pacman/errors"
+ "xorm.io/xorm"
)
type badgeRepo struct {
@@ -90,10 +92,29 @@ func (r *badgeRepo) ListByLevelAndGroup(ctx
context.Context, level entity.BadgeL
return
}
+// ListPaged returns a list of activated badges
+func (r *badgeRepo) ListPaged(ctx context.Context, page int, pageSize int)
(badges []*entity.Badge, total int64, err error) {
+ badges = make([]*entity.Badge, 0)
+ session := r.data.DB.Context(ctx).Where("status <> ?",
entity.BadgeStatusDeleted)
+ total, err = pager.Help(page, pageSize, &badges, &entity.Badge{},
session)
+ if err != nil {
+ err =
errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+ }
+ return
+}
+
// ListActivated returns a list of activated badges
-func (r *badgeRepo) ListActivated(ctx context.Context) (badges
[]*entity.Badge, err error) {
+func (r *badgeRepo) ListActivated(ctx context.Context, page int, pageSize int)
(badges []*entity.Badge, total int64, err error) {
badges = make([]*entity.Badge, 0)
- err = r.data.DB.Context(ctx).Where("status = ?",
entity.BadgeStatusActive).Find(&badges)
+ total = 0
+
+ session := r.data.DB.Context(ctx).Where("status = ?",
entity.BadgeStatusActive)
+ if page == 0 || pageSize == 0 {
+ err = session.Find(&badges)
+ } else {
+ total, err = pager.Help(page, pageSize, &badges,
&entity.Badge{}, session)
+ }
+
if err != nil {
err =
errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@@ -101,9 +122,17 @@ func (r *badgeRepo) ListActivated(ctx context.Context)
(badges []*entity.Badge,
}
// ListInactivated returns a list of inactivated badges
-func (r *badgeRepo) ListInactivated(ctx context.Context) (badges
[]*entity.Badge, err error) {
+func (r *badgeRepo) ListInactivated(ctx context.Context, page int, pageSize
int) (badges []*entity.Badge, total int64, err error) {
badges = make([]*entity.Badge, 0)
- err = r.data.DB.Context(ctx).Where("status = ?",
entity.BadgeStatusInactive).Find(&badges)
+ total = 0
+
+ session := r.data.DB.Context(ctx).Where("status = ?",
entity.BadgeStatusInactive)
+ if page == 0 || pageSize == 0 {
+ err = session.Find(&badges)
+ } else {
+ total, err = pager.Help(page, pageSize, &badges,
&entity.Badge{}, session)
+ }
+
if err != nil {
err =
errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@@ -115,3 +144,28 @@ func (r *badgeRepo) UpdateAwardCount(ctx context.Context,
id string, count int64
_, err = r.data.DB.Context(ctx).Where("id = ?", id).Incr("award_count",
count).Update(&entity.Badge{})
return
}
+
+// UpdateStatus updates the award count of a badge
+func (r *badgeRepo) UpdateStatus(ctx context.Context, id string, status int8)
(err error) {
+ _, err = r.data.DB.Transaction(func(session *xorm.Session) (result any,
err error) {
+ _, err = session.ID(id).Update(&entity.Badge{
+ Status: status,
+ })
+ if err != nil {
+ err =
errors.InternalServer(reason.DatabaseError).WithError(session.Rollback()).WithStack()
+ return
+ }
+ if status >= entity.BadgeStatusDeleted {
+ _, err = session.Where("badge_id = ?",
id).Cols("is_badge_deleted").Update(&entity.BadgeAward{
+ IsBadgeDeleted: entity.IsBadgeDeleted,
+ })
+ } else {
+ _, err = session.Where("badge_id = ?",
id).Cols("is_badge_deleted").Update(&entity.BadgeAward{
+ IsBadgeDeleted: entity.IsBadgeNotDeleted,
+ })
+ }
+ return
+ })
+
+ return
+}
diff --git a/internal/router/answer_api_router.go
b/internal/router/answer_api_router.go
index f343d7d1..b4ec3bdc 100644
--- a/internal/router/answer_api_router.go
+++ b/internal/router/answer_api_router.go
@@ -56,6 +56,7 @@ type AnswerAPIRouter struct {
reviewController *controller.ReviewController
metaController *controller.MetaController
badgeController *controller.BadgeController
+ adminBadgeController *controller_admin.BadgeController
}
func NewAnswerAPIRouter(
@@ -88,6 +89,7 @@ func NewAnswerAPIRouter(
reviewController *controller.ReviewController,
metaController *controller.MetaController,
badgeController *controller.BadgeController,
+ adminBadgeController *controller_admin.BadgeController,
) *AnswerAPIRouter {
return &AnswerAPIRouter{
langController: langController,
@@ -119,6 +121,7 @@ func NewAnswerAPIRouter(
reviewController: reviewController,
metaController: metaController,
badgeController: badgeController,
+ adminBadgeController: adminBadgeController,
}
}
@@ -369,4 +372,8 @@ func (a *AnswerAPIRouter) RegisterAnswerAdminAPIRouter(r
*gin.RouterGroup) {
r.PUT("/plugin/status", a.pluginController.UpdatePluginStatus)
r.GET("/plugin/config", a.pluginController.GetPluginConfig)
r.PUT("/plugin/config", a.pluginController.UpdatePluginConfig)
+
+ // badge
+ r.GET("/badges", a.adminBadgeController.GetBadgeList)
+ r.PUT("/badge/status", a.adminBadgeController.UpdateBadgeStatus)
}
diff --git a/internal/schema/badge_schema.go b/internal/schema/badge_schema.go
index cae2aa66..39a2e2ae 100644
--- a/internal/schema/badge_schema.go
+++ b/internal/schema/badge_schema.go
@@ -21,6 +21,23 @@ package schema
import "github.com/apache/incubator-answer/internal/entity"
+const (
+ BadgeStatusActive BadgeStatus = "active"
+ BadgeStatusInactive BadgeStatus = "inactive"
+)
+
+type BadgeStatus string
+
+var BadgeStatusMap = map[int8]BadgeStatus{
+ entity.BadgeStatusActive: BadgeStatusActive,
+ entity.BadgeStatusInactive: BadgeStatusInactive,
+}
+
+var BadgeStatusEMap = map[BadgeStatus]int8{
+ BadgeStatusActive: entity.BadgeStatusActive,
+ BadgeStatusInactive: entity.BadgeStatusInactive,
+}
+
// BadgeListInfo get badge list response
type BadgeListInfo struct {
// badge id
@@ -44,6 +61,43 @@ type GetBadgeListResp struct {
GroupName string `json:"group_name" `
}
+type UpdateBadgeStatusReq struct {
+ // badge id
+ ID string `validate:"required" json:"id"`
+ // badge status
+ Status BadgeStatus `validate:"required" json:"status"`
+}
+
+type GetBadgeListPagedReq struct {
+ // page
+ Page int `validate:"omitempty,min=1" form:"page"`
+ // page size
+ PageSize int `validate:"omitempty,min=1" form:"page_size"`
+ // badge status
+ Status BadgeStatus `validate:"omitempty" form:"status"`
+}
+
+type GetBadgeListPagedResp struct {
+ // badge id
+ ID string `json:"id" `
+ // badge name
+ Name string `json:"name" `
+ // badge description
+ Description string `json:"description" `
+ // badge icon
+ Icon string `json:"icon" `
+ // badge award count
+ AwardCount int `json:"award_count" `
+ // badge earned count
+ Earned bool `json:"earned" `
+ // badge level
+ Level entity.BadgeLevel `json:"level" `
+ // badge group name
+ GroupName string `json:"group_name" `
+ // badge status
+ Status BadgeStatus `json:"status"`
+}
+
type GetBadgeInfoResp struct {
// badge id
ID string `json:"id" `
diff --git a/internal/service/badge/badge_service.go
b/internal/service/badge/badge_service.go
index 32a54a8d..7daab796 100644
--- a/internal/service/badge/badge_service.go
+++ b/internal/service/badge/badge_service.go
@@ -39,10 +39,12 @@ type BadgeRepo interface {
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)
- ListActivated(ctx context.Context) ([]*entity.Badge, error)
- ListInactivated(ctx context.Context) ([]*entity.Badge, error)
+ ListPaged(ctx context.Context, page int, pageSize int) (badges
[]*entity.Badge, total int64, err error)
+ ListActivated(ctx context.Context, page int, pageSize int) (badges
[]*entity.Badge, total int64, err error)
+ ListInactivated(ctx context.Context, page int, pageSize int) (badges
[]*entity.Badge, total int64, err error)
- UpdateAwardCount(ctx context.Context, id string, count int64) error
+ UpdateAwardCount(ctx context.Context, id string, count int64) (err
error)
+ UpdateStatus(ctx context.Context, id string, status int8) (err error)
}
type BadgeService struct {
@@ -82,7 +84,7 @@ func (b *BadgeService) ListByGroup(ctx context.Context,
userID string) (resp []*
if err != nil {
return
}
- badges, err = b.badgeRepo.ListActivated(ctx)
+ badges, _, err = b.badgeRepo.ListActivated(ctx, 0, 0)
if err != nil {
return
}
@@ -95,7 +97,7 @@ func (b *BadgeService) ListByGroup(ctx context.Context,
userID string) (resp []*
}
for _, group := range groups {
- groupMap[converter.StringToInt64(group.ID)] = group.Name
+ groupMap[converter.StringToInt64(group.ID)] =
translator.Tr(handler.GetLangByCtx(ctx), group.Name)
}
for _, badge := range badges {
@@ -122,7 +124,7 @@ func (b *BadgeService) ListByGroup(ctx context.Context,
userID string) (resp []*
for _, group := range groups {
resp = append(resp, &schema.GetBadgeListResp{
- GroupName: group.Name,
+ GroupName: translator.Tr(handler.GetLangByCtx(ctx),
group.Name),
Badges: badgesMap[converter.StringToInt64(group.ID)],
})
}
@@ -130,6 +132,53 @@ func (b *BadgeService) ListByGroup(ctx context.Context,
userID string) (resp []*
return
}
+// ListPaged list all badges by page
+func (b *BadgeService) ListPaged(ctx context.Context, req
*schema.GetBadgeListPagedReq) (resp []*schema.GetBadgeListPagedResp, total
int64, err error) {
+ var (
+ groups []*entity.BadgeGroup
+ badges []*entity.Badge
+ groupMap = make(map[int64]string, 0)
+ )
+
+ switch req.Status {
+ case schema.BadgeStatusActive:
+ badges, total, err = b.badgeRepo.ListActivated(ctx, req.Page,
req.PageSize)
+ case schema.BadgeStatusInactive:
+ badges, total, err = b.badgeRepo.ListInactivated(ctx, req.Page,
req.PageSize)
+ default:
+ badges, total, err = b.badgeRepo.ListPaged(ctx, req.Page,
req.PageSize)
+ }
+
+ if err != nil {
+ return
+ }
+
+ // find all group and build group map
+ groups, err = b.badgeGroupRepo.ListGroups(ctx)
+ if err != nil {
+ return
+ }
+ for _, group := range groups {
+ groupMap[converter.StringToInt64(group.ID)] =
translator.Tr(handler.GetLangByCtx(ctx), group.Name)
+ }
+
+ resp = make([]*schema.GetBadgeListPagedResp, len(badges))
+
+ for i, badge := range badges {
+ resp[i] = &schema.GetBadgeListPagedResp{
+ 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,
+ Level: badge.Level,
+ GroupName: groupMap[badge.BadgeGroupID],
+ Status: schema.BadgeStatusMap[badge.Status],
+ }
+ }
+ return
+}
+
// GetBadgeInfo get badge info
func (b *BadgeService) GetBadgeInfo(ctx *gin.Context, id string, userID
string) (info *schema.GetBadgeInfoResp, err error) {
var (
@@ -164,3 +213,35 @@ func (b *BadgeService) GetBadgeInfo(ctx *gin.Context, id
string, userID string)
}
return
}
+
+// UpdateStatus update badge status
+func (b *BadgeService) UpdateStatus(ctx *gin.Context, req
*schema.UpdateBadgeStatusReq) (err error) {
+ var (
+ badge *entity.Badge
+ exists bool
+ )
+ req.ID = uid.DeShortID(req.ID)
+
+ badge, exists, err = b.badgeRepo.GetByID(ctx, req.ID)
+ if err != nil {
+ return
+ }
+ if !exists {
+ err = errors.BadRequest(reason.BadgeObjectNotFound)
+ return
+ }
+
+ status, ok := schema.BadgeStatusEMap[req.Status]
+ // check duplicate action
+ if badge.Status == status {
+ return
+ }
+
+ if !ok {
+ err = errors.BadRequest(reason.StatusInvalid)
+ return
+ }
+
+ err = b.badgeRepo.UpdateStatus(ctx, req.ID, status)
+ return
+}