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
+}


Reply via email to