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 ea43edc8 feat(badge): define func and add badge's list
ea43edc8 is described below

commit ea43edc864e718eb22e49e4e6576b958ab071d85
Author: kumfo <[email protected]>
AuthorDate: Wed Aug 7 16:35:22 2024 +0800

    feat(badge): define func and add badge's list
---
 cmd/wire_gen.go                                    |  11 +-
 docs/docs.go                                       | 137 ++++++++++++++++++--
 docs/swagger.json                                  | 139 +++++++++++++++++++--
 docs/swagger.yaml                                  |  89 +++++++++++--
 internal/controller/badge_controller.go            |  52 ++++++++
 internal/controller/controller.go                  |   1 +
 internal/entity/badge.go                           |  58 ---------
 .../{badge_award.go => badge_award_entity.go}      |  10 ++
 internal/entity/badge_entity.go                    |  60 +++++++++
 .../{badge_group.go => badge_group_entity.go}      |   0
 internal/repo/badge/badge_repo.go                  |  76 +++++++++++
 internal/repo/badge_award/badge_award_repo.go      | 106 ++++++++++++++++
 internal/repo/badge_group/badge_group_repo.go      |  50 ++++++++
 internal/repo/provider.go                          |   6 +
 internal/router/answer_api_router.go               |   6 +
 .../{entity/badge_group.go => schema/badge.go}     |  23 ++--
 internal/service/badge/badge_service.go            | 112 +++++++++++++++++
 .../service/badge_award/badge_award_service.go     |  64 ++++++++++
 .../badge_group/badge_group_service.go}            |  27 ++--
 internal/service/provider.go                       |   6 +
 20 files changed, 928 insertions(+), 105 deletions(-)

diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go
index 4928a122..c20aa2cf 100644
--- a/cmd/wire_gen.go
+++ b/cmd/wire_gen.go
@@ -40,6 +40,9 @@ import (
        "github.com/apache/incubator-answer/internal/repo/activity_common"
        "github.com/apache/incubator-answer/internal/repo/answer"
        "github.com/apache/incubator-answer/internal/repo/auth"
+       "github.com/apache/incubator-answer/internal/repo/badge"
+       "github.com/apache/incubator-answer/internal/repo/badge_award"
+       "github.com/apache/incubator-answer/internal/repo/badge_group"
        "github.com/apache/incubator-answer/internal/repo/captcha"
        "github.com/apache/incubator-answer/internal/repo/collection"
        "github.com/apache/incubator-answer/internal/repo/comment"
@@ -71,6 +74,7 @@ import (
        "github.com/apache/incubator-answer/internal/service/activity_queue"
        "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"
        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"
@@ -253,7 +257,12 @@ func initApplication(debug bool, serverConf *conf.Server, 
dbConf *data.Database,
        reviewController := controller.NewReviewController(reviewService, 
rankService, captchaService)
        metaService := meta2.NewMetaService(metaCommonService, userCommon, 
answerRepo, questionRepo)
        metaController := controller.NewMetaController(metaService)
-       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 [...]
+       badgeRepo := badge.NewBadgeRepo(dataData, uniqueIDRepo)
+       badgeGroupRepo := badge_group.NewBadgeGroupRepo(dataData, uniqueIDRepo)
+       badgeAwardRepo := badge_award.NewBadgeAwardRepo(dataData, uniqueIDRepo)
+       badgeService := badge2.NewBadgeService(badgeRepo, badgeGroupRepo, 
badgeAwardRepo)
+       badgeController := controller.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 cf1264d1..6fc76693 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -2239,6 +2239,49 @@ const docTemplate = `{
                 }
             }
         },
+        "/answer/api/v1/badges": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "list all badges group by group",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "api-badge"
+                ],
+                "summary": "list all badges group by group",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/handler.RespBody"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": 
"#/definitions/schema.GetBadgeListResp"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/answer/api/v1/collection/switch": {
             "post": {
                 "security": [
@@ -4410,7 +4453,7 @@ const docTemplate = `{
                                         "data": {
                                             "type": "array",
                                             "items": {
-                                                "$ref": 
"#/definitions/schema.GetTagResp"
+                                                "$ref": 
"#/definitions/schema.GetTagBasicResp"
                                             }
                                         }
                                     }
@@ -5380,7 +5423,7 @@ const docTemplate = `{
         },
         "/answer/api/v1/tags": {
             "get": {
-                "description": "get tags list",
+                "description": "get tags list by slug name",
                 "produces": [
                     "application/json"
                 ],
@@ -5404,7 +5447,22 @@ const docTemplate = `{
                     "200": {
                         "description": "OK",
                         "schema": {
-                            "$ref": "#/definitions/handler.RespBody"
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/handler.RespBody"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": 
"#/definitions/schema.GetTagBasicResp"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
                         }
                     }
                 }
@@ -7327,6 +7385,26 @@ const docTemplate = `{
                 }
             }
         },
+        "schema.BadgeListInfo": {
+            "type": "object",
+            "properties": {
+                "award_count": {
+                    "type": "integer"
+                },
+                "earned": {
+                    "type": "boolean"
+                },
+                "icon": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                }
+            }
+        },
         "schema.CloseQuestionReq": {
             "type": "object",
             "required": [
@@ -7578,6 +7656,20 @@ const docTemplate = `{
                 }
             }
         },
+        "schema.GetBadgeListResp": {
+            "type": "object",
+            "properties": {
+                "badges": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/schema.BadgeListInfo"
+                    }
+                },
+                "group_name": {
+                    "type": "string"
+                }
+            }
+        },
         "schema.GetCommentPersonalWithPageResp": {
             "type": "object",
             "properties": {
@@ -8242,6 +8334,23 @@ const docTemplate = `{
                 }
             }
         },
+        "schema.GetTagBasicResp": {
+            "type": "object",
+            "properties": {
+                "display_name": {
+                    "type": "string"
+                },
+                "recommend": {
+                    "type": "boolean"
+                },
+                "reserved": {
+                    "type": "boolean"
+                },
+                "slug_name": {
+                    "type": "string"
+                }
+            }
+        },
         "schema.GetTagPageResp": {
             "type": "object",
             "properties": {
@@ -9819,7 +9928,7 @@ const docTemplate = `{
                 "recommend_tags": {
                     "type": "array",
                     "items": {
-                        "type": "string"
+                        "$ref": "#/definitions/schema.SiteWriteTag"
                     }
                 },
                 "required_tag": {
@@ -9828,7 +9937,7 @@ const docTemplate = `{
                 "reserved_tags": {
                     "type": "array",
                     "items": {
-                        "type": "string"
+                        "$ref": "#/definitions/schema.SiteWriteTag"
                     }
                 },
                 "restrict_answer": {
@@ -9842,7 +9951,7 @@ const docTemplate = `{
                 "recommend_tags": {
                     "type": "array",
                     "items": {
-                        "type": "string"
+                        "$ref": "#/definitions/schema.SiteWriteTag"
                     }
                 },
                 "required_tag": {
@@ -9851,7 +9960,7 @@ const docTemplate = `{
                 "reserved_tags": {
                     "type": "array",
                     "items": {
-                        "type": "string"
+                        "$ref": "#/definitions/schema.SiteWriteTag"
                     }
                 },
                 "restrict_answer": {
@@ -9859,6 +9968,20 @@ const docTemplate = `{
                 }
             }
         },
+        "schema.SiteWriteTag": {
+            "type": "object",
+            "required": [
+                "slug_name"
+            ],
+            "properties": {
+                "display_name": {
+                    "type": "string"
+                },
+                "slug_name": {
+                    "type": "string"
+                }
+            }
+        },
         "schema.TagItem": {
             "type": "object",
             "properties": {
diff --git a/docs/swagger.json b/docs/swagger.json
index 1e93d2b0..45936363 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -2209,6 +2209,49 @@
                 }
             }
         },
+        "/answer/api/v1/badges": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "list all badges group by group",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "api-badge"
+                ],
+                "summary": "list all badges group by group",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/handler.RespBody"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": 
"#/definitions/schema.GetBadgeListResp"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/answer/api/v1/collection/switch": {
             "post": {
                 "security": [
@@ -4380,7 +4423,7 @@
                                         "data": {
                                             "type": "array",
                                             "items": {
-                                                "$ref": 
"#/definitions/schema.GetTagResp"
+                                                "$ref": 
"#/definitions/schema.GetTagBasicResp"
                                             }
                                         }
                                     }
@@ -5350,7 +5393,7 @@
         },
         "/answer/api/v1/tags": {
             "get": {
-                "description": "get tags list",
+                "description": "get tags list by slug name",
                 "produces": [
                     "application/json"
                 ],
@@ -5374,7 +5417,22 @@
                     "200": {
                         "description": "OK",
                         "schema": {
-                            "$ref": "#/definitions/handler.RespBody"
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/handler.RespBody"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": 
"#/definitions/schema.GetTagBasicResp"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
                         }
                     }
                 }
@@ -7297,6 +7355,26 @@
                 }
             }
         },
+        "schema.BadgeListInfo": {
+            "type": "object",
+            "properties": {
+                "award_count": {
+                    "type": "integer"
+                },
+                "earned": {
+                    "type": "boolean"
+                },
+                "icon": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                }
+            }
+        },
         "schema.CloseQuestionReq": {
             "type": "object",
             "required": [
@@ -7548,6 +7626,20 @@
                 }
             }
         },
+        "schema.GetBadgeListResp": {
+            "type": "object",
+            "properties": {
+                "badges": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/definitions/schema.BadgeListInfo"
+                    }
+                },
+                "group_name": {
+                    "type": "string"
+                }
+            }
+        },
         "schema.GetCommentPersonalWithPageResp": {
             "type": "object",
             "properties": {
@@ -8212,6 +8304,23 @@
                 }
             }
         },
+        "schema.GetTagBasicResp": {
+            "type": "object",
+            "properties": {
+                "display_name": {
+                    "type": "string"
+                },
+                "recommend": {
+                    "type": "boolean"
+                },
+                "reserved": {
+                    "type": "boolean"
+                },
+                "slug_name": {
+                    "type": "string"
+                }
+            }
+        },
         "schema.GetTagPageResp": {
             "type": "object",
             "properties": {
@@ -8809,7 +8918,7 @@
                     "enum": [
                         "newest",
                         "active",
-                        "frequent",
+                        "hot",
                         "score",
                         "unanswered"
                     ]
@@ -9789,7 +9898,7 @@
                 "recommend_tags": {
                     "type": "array",
                     "items": {
-                        "type": "string"
+                        "$ref": "#/definitions/schema.SiteWriteTag"
                     }
                 },
                 "required_tag": {
@@ -9798,7 +9907,7 @@
                 "reserved_tags": {
                     "type": "array",
                     "items": {
-                        "type": "string"
+                        "$ref": "#/definitions/schema.SiteWriteTag"
                     }
                 },
                 "restrict_answer": {
@@ -9812,7 +9921,7 @@
                 "recommend_tags": {
                     "type": "array",
                     "items": {
-                        "type": "string"
+                        "$ref": "#/definitions/schema.SiteWriteTag"
                     }
                 },
                 "required_tag": {
@@ -9821,7 +9930,7 @@
                 "reserved_tags": {
                     "type": "array",
                     "items": {
-                        "type": "string"
+                        "$ref": "#/definitions/schema.SiteWriteTag"
                     }
                 },
                 "restrict_answer": {
@@ -9829,6 +9938,20 @@
                 }
             }
         },
+        "schema.SiteWriteTag": {
+            "type": "object",
+            "required": [
+                "slug_name"
+            ],
+            "properties": {
+                "display_name": {
+                    "type": "string"
+                },
+                "slug_name": {
+                    "type": "string"
+                }
+            }
+        },
         "schema.TagItem": {
             "type": "object",
             "properties": {
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 93f8116a..c91c987e 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -342,6 +342,19 @@ definitions:
         maxLength: 100
         type: string
     type: object
+  schema.BadgeListInfo:
+    properties:
+      award_count:
+        type: integer
+      earned:
+        type: boolean
+      icon:
+        type: string
+      id:
+        type: string
+      name:
+        type: string
+    type: object
   schema.CloseQuestionReq:
     properties:
       close_msg:
@@ -513,6 +526,15 @@ definitions:
         description: if user is followed object will be true,otherwise false
         type: boolean
     type: object
+  schema.GetBadgeListResp:
+    properties:
+      badges:
+        items:
+          $ref: '#/definitions/schema.BadgeListInfo'
+        type: array
+      group_name:
+        type: string
+    type: object
   schema.GetCommentPersonalWithPageResp:
     properties:
       answer_id:
@@ -983,6 +1005,17 @@ definitions:
       terms_of_service_parsed_text:
         type: string
     type: object
+  schema.GetTagBasicResp:
+    properties:
+      display_name:
+        type: string
+      recommend:
+        type: boolean
+      reserved:
+        type: boolean
+      slug_name:
+        type: string
+    type: object
   schema.GetTagPageResp:
     properties:
       created_at:
@@ -1401,7 +1434,7 @@ definitions:
         enum:
         - newest
         - active
-        - frequent
+        - hot
         - score
         - unanswered
         type: string
@@ -2071,13 +2104,13 @@ definitions:
     properties:
       recommend_tags:
         items:
-          type: string
+          $ref: '#/definitions/schema.SiteWriteTag'
         type: array
       required_tag:
         type: boolean
       reserved_tags:
         items:
-          type: string
+          $ref: '#/definitions/schema.SiteWriteTag'
         type: array
       restrict_answer:
         type: boolean
@@ -2086,17 +2119,26 @@ definitions:
     properties:
       recommend_tags:
         items:
-          type: string
+          $ref: '#/definitions/schema.SiteWriteTag'
         type: array
       required_tag:
         type: boolean
       reserved_tags:
         items:
-          type: string
+          $ref: '#/definitions/schema.SiteWriteTag'
         type: array
       restrict_answer:
         type: boolean
     type: object
+  schema.SiteWriteTag:
+    properties:
+      display_name:
+        type: string
+      slug_name:
+        type: string
+    required:
+    - slug_name
+    type: object
   schema.TagItem:
     properties:
       display_name:
@@ -4063,6 +4105,30 @@ paths:
       summary: recover answer
       tags:
       - Answer
+  /answer/api/v1/badges:
+    get:
+      consumes:
+      - application/json
+      description: list all badges group by group
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/handler.RespBody'
+            - properties:
+                data:
+                  items:
+                    $ref: '#/definitions/schema.GetBadgeListResp'
+                  type: array
+              type: object
+      security:
+      - ApiKeyAuth: []
+      summary: list all badges group by group
+      tags:
+      - api-badge
   /answer/api/v1/collection/switch:
     post:
       consumes:
@@ -5382,7 +5448,7 @@ paths:
             - properties:
                 data:
                   items:
-                    $ref: '#/definitions/schema.GetTagResp'
+                    $ref: '#/definitions/schema.GetTagBasicResp'
                   type: array
               type: object
       security:
@@ -5965,7 +6031,7 @@ paths:
       - Tag
   /answer/api/v1/tags:
     get:
-      description: get tags list
+      description: get tags list by slug name
       parameters:
       - collectionFormat: csv
         description: string collection
@@ -5980,7 +6046,14 @@ paths:
         "200":
           description: OK
           schema:
-            $ref: '#/definitions/handler.RespBody'
+            allOf:
+            - $ref: '#/definitions/handler.RespBody'
+            - properties:
+                data:
+                  items:
+                    $ref: '#/definitions/schema.GetTagBasicResp'
+                  type: array
+              type: object
       summary: get tags list
       tags:
       - Tag
diff --git a/internal/controller/badge_controller.go 
b/internal/controller/badge_controller.go
new file mode 100644
index 00000000..ebc534ce
--- /dev/null
+++ b/internal/controller/badge_controller.go
@@ -0,0 +1,52 @@
+/*
+ * 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
+
+import (
+       "github.com/apache/incubator-answer/internal/base/handler"
+       "github.com/apache/incubator-answer/internal/base/middleware"
+       "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
+// @Summary list all badges group by group
+// @Description list all badges group by group
+// @Tags api-badge
+// @Accept json
+// @Produce json
+// @Security ApiKeyAuth
+// @Success 200 {object} handler.RespBody{data=[]schema.GetBadgeListResp}
+// @Router /answer/api/v1/badges [get]
+func (b *BadgeController) GetBadgeList(ctx *gin.Context) {
+       userID := middleware.GetLoginUserIDFromContext(ctx)
+       resp, err := b.badgeService.ListByGroup(ctx, userID)
+       handler.HandleResponse(ctx, err, resp)
+}
diff --git a/internal/controller/controller.go 
b/internal/controller/controller.go
index 9eb64c58..8fad918a 100644
--- a/internal/controller/controller.go
+++ b/internal/controller/controller.go
@@ -51,4 +51,5 @@ var ProviderSetController = wire.NewSet(
        NewCaptchaController,
        NewMetaController,
        NewEmbedController,
+       NewBadgeController,
 )
diff --git a/internal/entity/badge.go b/internal/entity/badge.go
deleted file mode 100644
index 976bb47d..00000000
--- a/internal/entity/badge.go
+++ /dev/null
@@ -1,58 +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 entity
-
-import "time"
-
-const (
-       BadgeStatusActive   = 1
-       BadgeStatusDeleted  = 10
-       BadgeStatusInactive = 11
-
-       BadgeLevelBronze = 1
-       BadgeLevelSilver = 2
-       BadgeLevelGold   = 3
-
-       BadgeSingleAward = 1
-       BadgeMultiAward  = 2
-)
-
-// Badge badge
-type Badge 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"`
-       Name         string    `json:"name" xorm:"not null default '' 
VARCHAR(256) name"`
-       Icon         string    `json:"icon" xorm:"not null default '' 
VARCHAR(1024) icon"`
-       AwardCount   int64     `json:"award_count" xorm:"not null default 0 
INT(11) award_count"`
-       Description  string    `json:"description" xorm:"not null default '' 
MEDIUMTEXT description"`
-       Status       int8      `json:"status" xorm:"not null default 1 INT(11) 
status"`
-       BadgeGroupId int64     `json:"badge_group_id" xorm:"not null default 0 
BIGINT(20) badge_group_id"`
-       Level        int64     `json:"level" xorm:"not null default 1 
TINYINT(4) level"`
-       Single       int8      `json:"single" xorm:"not null default 1 
TINYINT(4) single"`
-       Collect      string    `json:"collect" xorm:"not null default '' 
VARCHAR(64) collect"`
-       Handler      string    `json:"handler" xorm:"not null default '' 
VARCHAR(64) handler"`
-       Param        string    `json:"param" xorm:"not null default '' 
VARCHAR(128) param"`
-}
-
-// TableName badge table name
-func (Badge) TableName() string {
-       return "badge"
-}
diff --git a/internal/entity/badge_award.go 
b/internal/entity/badge_award_entity.go
similarity index 88%
rename from internal/entity/badge_award.go
rename to internal/entity/badge_award_entity.go
index 235d369c..a852f6bd 100644
--- a/internal/entity/badge_award.go
+++ b/internal/entity/badge_award_entity.go
@@ -37,3 +37,13 @@ type BadgeAward struct {
 func (BadgeAward) TableName() string {
        return "badge_award"
 }
+
+type BadgeEarnedCount struct {
+       BadgeID     string `xorm:"badge_id"`
+       EarnedCount int    `xorm:"earned_count"`
+}
+
+// TableName badge_award table name
+func (BadgeEarnedCount) TableName() string {
+       return "badge_award"
+}
diff --git a/internal/entity/badge_entity.go b/internal/entity/badge_entity.go
new file mode 100644
index 00000000..da6cd4dd
--- /dev/null
+++ b/internal/entity/badge_entity.go
@@ -0,0 +1,60 @@
+/*
+ * 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 entity
+
+import "time"
+
+type BadgeLevel int
+
+const (
+       BadgeStatusActive   = 1
+       BadgeStatusDeleted  = 10
+       BadgeStatusInactive = 11
+
+       BadgeLevelBronze BadgeLevel = 1
+       BadgeLevelSilver BadgeLevel = 2
+       BadgeLevelGold   BadgeLevel = 3
+
+       BadgeSingleAward = 1
+       BadgeMultiAward  = 2
+)
+
+// Badge badge
+type Badge 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"`
+       Name         string     `json:"name" xorm:"not null default '' 
VARCHAR(256) name"`
+       Icon         string     `json:"icon" xorm:"not null default '' 
VARCHAR(1024) icon"`
+       AwardCount   int        `json:"award_count" xorm:"not null default 0 
INT(11) award_count"`
+       Description  string     `json:"description" xorm:"not null default '' 
MEDIUMTEXT description"`
+       Status       int8       `json:"status" xorm:"not null default 1 INT(11) 
status"`
+       BadgeGroupId int64      `json:"badge_group_id" xorm:"not null default 0 
BIGINT(20) badge_group_id"`
+       Level        BadgeLevel `json:"level" xorm:"not null default 1 
TINYINT(4) level"`
+       Single       int8       `json:"single" xorm:"not null default 1 
TINYINT(4) single"`
+       Collect      string     `json:"collect" xorm:"not null default '' 
VARCHAR(64) collect"`
+       Handler      string     `json:"handler" xorm:"not null default '' 
VARCHAR(64) handler"`
+       Param        string     `json:"param" xorm:"not null default '' 
VARCHAR(128) param"`
+}
+
+// TableName badge table name
+func (Badge) TableName() string {
+       return "badge"
+}
diff --git a/internal/entity/badge_group.go 
b/internal/entity/badge_group_entity.go
similarity index 100%
copy from internal/entity/badge_group.go
copy to internal/entity/badge_group_entity.go
diff --git a/internal/repo/badge/badge_repo.go 
b/internal/repo/badge/badge_repo.go
new file mode 100644
index 00000000..b4cd72f1
--- /dev/null
+++ b/internal/repo/badge/badge_repo.go
@@ -0,0 +1,76 @@
+/*
+ * 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 badge
+
+import (
+       "context"
+       "github.com/apache/incubator-answer/internal/base/data"
+       "github.com/apache/incubator-answer/internal/entity"
+       "github.com/apache/incubator-answer/internal/service/badge"
+       "github.com/apache/incubator-answer/internal/service/unique"
+)
+
+type badgeRepo struct {
+       data         *data.Data
+       uniqueIDRepo unique.UniqueIDRepo
+}
+
+// NewBadgeRepo creates a new badge repository
+func NewBadgeRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo) 
badge.BadgeRepo {
+       return &badgeRepo{
+               data:         data,
+               uniqueIDRepo: uniqueIDRepo,
+       }
+}
+
+// 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)
+       err = r.data.DB.Context(ctx).Where("level = ?", level).Find(&badges)
+       return
+}
+
+// ListByGroup returns a list of badges by group
+func (r *badgeRepo) ListByGroup(ctx context.Context, groupID int64) (badges 
[]*entity.Badge, err error) {
+       badges = make([]*entity.Badge, 0)
+       err = r.data.DB.Context(ctx).Where("group_id = ?", 
groupID).Find(&badges)
+       return
+}
+
+// ListByLevelAndGroup returns a list of badges by level and group
+func (r *badgeRepo) ListByLevelAndGroup(ctx context.Context, level 
entity.BadgeLevel, groupID int64) (badges []*entity.Badge, err error) {
+       badges = make([]*entity.Badge, 0)
+       err = r.data.DB.Context(ctx).Where("level = ? AND group_id = ?", level, 
groupID).Find(&badges)
+       return
+}
+
+// ListActivated returns a list of activated badges
+func (r *badgeRepo) ListActivated(ctx context.Context) (badges 
[]*entity.Badge, err error) {
+       badges = make([]*entity.Badge, 0)
+       err = r.data.DB.Context(ctx).Where("status = ?", 
entity.BadgeStatusActive).Find(&badges)
+       return
+}
+
+// ListInactivated returns a list of inactivated badges
+func (r *badgeRepo) ListInactivated(ctx context.Context) (badges 
[]*entity.Badge, err error) {
+       badges = make([]*entity.Badge, 0)
+       err = r.data.DB.Context(ctx).Where("status = ?", 
entity.BadgeStatusInactive).Find(&badges)
+       return
+}
diff --git a/internal/repo/badge_award/badge_award_repo.go 
b/internal/repo/badge_award/badge_award_repo.go
new file mode 100644
index 00000000..b6f0e95e
--- /dev/null
+++ b/internal/repo/badge_award/badge_award_repo.go
@@ -0,0 +1,106 @@
+/*
+ * 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 badge_award
+
+import (
+       "context"
+       "github.com/apache/incubator-answer/internal/base/data"
+       "github.com/apache/incubator-answer/internal/entity"
+       "github.com/apache/incubator-answer/internal/service/badge_award"
+       "github.com/apache/incubator-answer/internal/service/unique"
+       "time"
+)
+
+type badgeAwardRepo struct {
+       data         *data.Data
+       uniqueIDRepo unique.UniqueIDRepo
+}
+
+func NewBadgeAwardRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo) 
badge_award.BadgeAwardRepo {
+       return &badgeAwardRepo{
+               data:         data,
+               uniqueIDRepo: uniqueIDRepo,
+       }
+}
+
+func (r *badgeAwardRepo) Award(ctx context.Context, badgeID string, userID 
string, objectID string, force bool, createdAt time.Time) {
+       return
+}
+func (r *badgeAwardRepo) CheckIsAward(ctx context.Context, badgeID string, 
userID string, objectID string) (isAward bool) {
+       return
+}
+func (r *badgeAwardRepo) CountByUserIdAndBadgeLevel(ctx context.Context, 
userID string, badgeLevel entity.BadgeLevel) (awardCount int64) {
+       return
+}
+func (r *badgeAwardRepo) CountByUserId(ctx context.Context, userID string) 
(awardCount int64) {
+       return
+}
+func (r *badgeAwardRepo) CountByUserIdAndBadgeId(ctx context.Context, userID 
string, badgeID string) (awardCount int64) {
+       return
+}
+func (r *badgeAwardRepo) CountByObjectId(ctx context.Context, objectID string) 
(awardCount int64) {
+       return
+}
+func (r *badgeAwardRepo) CountByObjectIdAndBadgeId(ctx context.Context, 
objectID string, badgeID string) (awardCount int64) {
+       return
+}
+func (r *badgeAwardRepo) CountBadgesByUserIdAndObjectId(ctx context.Context, 
userID string, objectID string, badgeID string) (awardCount int64) {
+       return
+}
+func (r *badgeAwardRepo) SumUserEarnedGroupByBadgeID(ctx context.Context, 
userID string) (earnedCounts []*entity.BadgeEarnedCount, err error) {
+       err = r.data.DB.Context(ctx).Select("badge_id, count(`id`) AS 
earned_count").Where("user_id = ?", 
userID).GroupBy("badge_id").Find(&earnedCounts)
+       return
+}
+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) {
+       return
+}
+func (r *badgeAwardRepo) ListPagedByBadgeIdAndUserId(ctx context.Context, 
badgeID string, userID string, page int64, pageSize int64) (badgeAwards 
[]*entity.BadgeAward, total int64) {
+       return
+}
+func (r *badgeAwardRepo) ListPagedByObjectId(ctx context.Context, badgeID 
string, objectID string, page int64, pageSize int64) (badgeAwards 
[]*entity.BadgeAward, total int64) {
+       return
+}
+func (r *badgeAwardRepo) ListPagedByObjectIdAndUserId(ctx context.Context, 
badgeID string, objectID string, userID string, page int64, pageSize int64) 
(badgeAwards []*entity.BadgeAward, total int64) {
+       return
+}
+func (r *badgeAwardRepo) ListTagPagedByBadgeId(ctx context.Context, badgeIDs 
[]int64, page int64, pageSize int64, filterUserID int64) (badgeAwards 
[]*entity.BadgeAward, total int64) {
+       return
+}
+func (r *badgeAwardRepo) ListTagPagedByBadgeIdAndUserId(ctx context.Context, 
badgeIDs []int64, userID string, page int64, pageSize int64) (badgeAwards 
[]*entity.BadgeAward, total int64) {
+       return
+}
+func (r *badgeAwardRepo) ListPagedLatest(ctx context.Context, page int64, 
pageSize int64) (badgeAwards []*entity.BadgeAward, total int64) {
+       return
+}
+func (r *badgeAwardRepo) ListNewestEarnedByLevel(ctx context.Context, userID 
string, level entity.BadgeLevel, num int64) (badgeAwards []*entity.BadgeAward, 
total int64) {
+       return
+}
+func (r *badgeAwardRepo) ListNewestByUserIdAndLevel(ctx context.Context, 
userID string, level int64, page int64, pageSize int64) (badgeAwards 
[]*entity.BadgeAward, total int64) {
+       return
+}
+func (r *badgeAwardRepo) GetByUserIdAndBadgeId(ctx context.Context, userID 
string, badgeID string) (badgeAward *entity.BadgeAward) {
+       return
+}
+func (r *badgeAwardRepo) GetByUserIdAndBadgeIdAndObjectId(ctx context.Context, 
userID string, badgeID string, objectID string) (badgeAward *entity.BadgeAward) 
{
+       return
+}
diff --git a/internal/repo/badge_group/badge_group_repo.go 
b/internal/repo/badge_group/badge_group_repo.go
new file mode 100644
index 00000000..7dfe90fc
--- /dev/null
+++ b/internal/repo/badge_group/badge_group_repo.go
@@ -0,0 +1,50 @@
+/*
+ * 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 badge_group
+
+import (
+       "context"
+       "github.com/apache/incubator-answer/internal/base/data"
+       "github.com/apache/incubator-answer/internal/entity"
+       "github.com/apache/incubator-answer/internal/service/badge_group"
+       "github.com/apache/incubator-answer/internal/service/unique"
+)
+
+type badgeGroupRepo struct {
+       data         *data.Data
+       uniqueIDRepo unique.UniqueIDRepo
+}
+
+func NewBadgeGroupRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo) 
badge_group.BadgeGroupRepo {
+       return &badgeGroupRepo{
+               data:         data,
+               uniqueIDRepo: uniqueIDRepo,
+       }
+}
+
+func (r *badgeGroupRepo) ListGroups(ctx context.Context) (groups 
[]*entity.BadgeGroup, err error) {
+       groups = make([]*entity.BadgeGroup, 0)
+       err = r.data.DB.Context(ctx).Find(&groups)
+       return
+}
+
+func (r *badgeGroupRepo) AddGroup(ctx context.Context, group 
*entity.BadgeGroup) (err error) {
+       return
+}
diff --git a/internal/repo/provider.go b/internal/repo/provider.go
index 3a517120..ee309e02 100644
--- a/internal/repo/provider.go
+++ b/internal/repo/provider.go
@@ -25,6 +25,9 @@ import (
        "github.com/apache/incubator-answer/internal/repo/activity_common"
        "github.com/apache/incubator-answer/internal/repo/answer"
        "github.com/apache/incubator-answer/internal/repo/auth"
+       "github.com/apache/incubator-answer/internal/repo/badge"
+       "github.com/apache/incubator-answer/internal/repo/badge_award"
+       "github.com/apache/incubator-answer/internal/repo/badge_group"
        "github.com/apache/incubator-answer/internal/repo/captcha"
        "github.com/apache/incubator-answer/internal/repo/collection"
        "github.com/apache/incubator-answer/internal/repo/comment"
@@ -100,4 +103,7 @@ var ProviderSetRepo = wire.NewSet(
        limit.NewRateLimitRepo,
        plugin_config.NewPluginUserConfigRepo,
        review.NewReviewRepo,
+       badge.NewBadgeRepo,
+       badge_group.NewBadgeGroupRepo,
+       badge_award.NewBadgeAwardRepo,
 )
diff --git a/internal/router/answer_api_router.go 
b/internal/router/answer_api_router.go
index 32854186..dadf2a85 100644
--- a/internal/router/answer_api_router.go
+++ b/internal/router/answer_api_router.go
@@ -55,6 +55,7 @@ type AnswerAPIRouter struct {
        userPluginController    *controller.UserPluginController
        reviewController        *controller.ReviewController
        metaController          *controller.MetaController
+       badgeController         *controller.BadgeController
 }
 
 func NewAnswerAPIRouter(
@@ -86,6 +87,7 @@ func NewAnswerAPIRouter(
        userPluginController *controller.UserPluginController,
        reviewController *controller.ReviewController,
        metaController *controller.MetaController,
+       badgeController *controller.BadgeController,
 ) *AnswerAPIRouter {
        return &AnswerAPIRouter{
                langController:          langController,
@@ -116,6 +118,7 @@ func NewAnswerAPIRouter(
                userPluginController:    userPluginController,
                reviewController:        reviewController,
                metaController:          metaController,
+               badgeController:         badgeController,
        }
 }
 
@@ -187,6 +190,9 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r 
*gin.RouterGroup) {
 
        // reaction
        r.GET("/meta/reaction", a.metaController.GetReaction)
+
+       // badges
+       r.GET("/badges", a.badgeController.GetBadgeList)
 }
 
 func (a *AnswerAPIRouter) RegisterAuthUserWithAnyStatusAnswerAPIRouter(r 
*gin.RouterGroup) {
diff --git a/internal/entity/badge_group.go b/internal/schema/badge.go
similarity index 59%
copy from internal/entity/badge_group.go
copy to internal/schema/badge.go
index 3be4d820..c52a235b 100644
--- a/internal/entity/badge_group.go
+++ b/internal/schema/badge.go
@@ -17,19 +17,18 @@
  * under the License.
  */
 
-package entity
+package schema
 
-import "time"
-
-// BadgeGroup badge_group
-type BadgeGroup struct {
-       ID        string    `json:"id" xorm:"not null pk autoincr BIGINT(20) 
id"`
-       Name      string    `json:"name" xorm:"not null default '' VARCHAR(256) 
name"`
-       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"`
+// 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" `
 }
 
-// TableName badge_group table name
-func (BadgeGroup) TableName() string {
-       return "badge_group"
+type GetBadgeListResp struct {
+       Badges    []*BadgeListInfo `json:"badges" `
+       GroupName string           `json:"group_name" `
 }
diff --git a/internal/service/badge/badge_service.go 
b/internal/service/badge/badge_service.go
new file mode 100644
index 00000000..1109b444
--- /dev/null
+++ b/internal/service/badge/badge_service.go
@@ -0,0 +1,112 @@
+/*
+ * 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 badge
+
+import (
+       "context"
+       "github.com/apache/incubator-answer/internal/base/handler"
+       "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"
+)
+
+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)
+}
+
+type BadgeService struct {
+       badgeRepo      BadgeRepo
+       badgeGroupRepo badge_group.BadgeGroupRepo
+       badgeAwardRepo badge_award.BadgeAwardRepo
+}
+
+func NewBadgeService(
+       badgeRepo BadgeRepo,
+       badgeGroupRepo badge_group.BadgeGroupRepo,
+       badgeAwardRepo badge_award.BadgeAwardRepo) *BadgeService {
+       return &BadgeService{
+               badgeRepo:      badgeRepo,
+               badgeGroupRepo: badgeGroupRepo,
+               badgeAwardRepo: badgeAwardRepo,
+       }
+}
+
+func (b *BadgeService) ListByGroup(ctx context.Context, userID string) (resp 
[]*schema.GetBadgeListResp, err error) {
+       var (
+               groups       []*entity.BadgeGroup
+               badges       []*entity.Badge
+               earnedCounts []*entity.BadgeEarnedCount
+
+               groupMap  = make(map[int64]string, 0)
+               badgesMap = make(map[int64][]*schema.BadgeListInfo, 0)
+       )
+       resp = make([]*schema.GetBadgeListResp, 0)
+
+       groups, err = b.badgeGroupRepo.ListGroups(ctx)
+       if err != nil {
+               return
+       }
+       badges, err = b.badgeRepo.ListActivated(ctx)
+       if err != nil {
+               return
+       }
+       earnedCounts, err = b.badgeAwardRepo.SumUserEarnedGroupByBadgeID(ctx, 
userID)
+
+       for _, group := range groups {
+               groupMap[converter.StringToInt64(group.ID)] = group.Name
+       }
+
+       for _, badge := range badges {
+               // check is earned
+               earned := false
+               if len(earnedCounts) > 0 {
+                       for _, earnedCount := range earnedCounts {
+                               if badge.ID == earnedCount.BadgeID {
+                                       earned = true
+                                       break
+                               }
+                       }
+               }
+
+               badgesMap[badge.BadgeGroupId] = 
append(badgesMap[badge.BadgeGroupId], &schema.BadgeListInfo{
+                       ID:         badge.ID,
+                       Name:       translator.Tr(handler.GetLangByCtx(ctx), 
badge.Name),
+                       Icon:       badge.Icon,
+                       AwardCount: badge.AwardCount,
+                       Earned:     earned,
+               })
+       }
+
+       for _, group := range groups {
+               resp = append(resp, &schema.GetBadgeListResp{
+                       GroupName: group.Name,
+                       Badges:    badgesMap[converter.StringToInt64(group.ID)],
+               })
+       }
+
+       return
+}
diff --git a/internal/service/badge_award/badge_award_service.go 
b/internal/service/badge_award/badge_award_service.go
new file mode 100644
index 00000000..8f84453e
--- /dev/null
+++ b/internal/service/badge_award/badge_award_service.go
@@ -0,0 +1,64 @@
+/*
+ * 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 badge_award
+
+import (
+       "context"
+       "github.com/apache/incubator-answer/internal/entity"
+       "time"
+)
+
+type BadgeAwardRepo interface {
+       Award(ctx context.Context, badgeID string, userID string, objectID 
string, force bool, createdAt time.Time)
+       CheckIsAward(ctx context.Context, badgeID string, userID string, 
objectID string) bool
+
+       CountByUserIdAndBadgeLevel(ctx context.Context, userID string, 
badgeLevel entity.BadgeLevel) (awardCount int64)
+       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
+       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)
+
+       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
+}
+
+func NewBadgeAwardService(badgeAwardRepo BadgeAwardRepo) *BadgeAwardService {
+       return &BadgeAwardService{
+               badgeAwardRepo: badgeAwardRepo,
+       }
+}
diff --git a/internal/entity/badge_group.go 
b/internal/service/badge_group/badge_group_service.go
similarity index 59%
rename from internal/entity/badge_group.go
rename to internal/service/badge_group/badge_group_service.go
index 3be4d820..c78f3e4f 100644
--- a/internal/entity/badge_group.go
+++ b/internal/service/badge_group/badge_group_service.go
@@ -17,19 +17,24 @@
  * under the License.
  */
 
-package entity
+package badge_group
 
-import "time"
+import (
+       "context"
+       "github.com/apache/incubator-answer/internal/entity"
+)
 
-// BadgeGroup badge_group
-type BadgeGroup struct {
-       ID        string    `json:"id" xorm:"not null pk autoincr BIGINT(20) 
id"`
-       Name      string    `json:"name" xorm:"not null default '' VARCHAR(256) 
name"`
-       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"`
+type BadgeGroupRepo interface {
+       ListGroups(ctx context.Context) (groups []*entity.BadgeGroup, err error)
+       AddGroup(ctx context.Context, group *entity.BadgeGroup) (err error)
 }
 
-// TableName badge_group table name
-func (BadgeGroup) TableName() string {
-       return "badge_group"
+type BadgeGroupService struct {
+       badgeGroupRepo BadgeGroupRepo
+}
+
+func NewBadgeGroupService(badgeGroupRepo BadgeGroupRepo) *BadgeGroupService {
+       return &BadgeGroupService{
+               badgeGroupRepo: badgeGroupRepo,
+       }
 }
diff --git a/internal/service/provider.go b/internal/service/provider.go
index e82d9316..38832923 100644
--- a/internal/service/provider.go
+++ b/internal/service/provider.go
@@ -26,6 +26,9 @@ import (
        "github.com/apache/incubator-answer/internal/service/activity_queue"
        answercommon 
"github.com/apache/incubator-answer/internal/service/answer_common"
        "github.com/apache/incubator-answer/internal/service/auth"
+       "github.com/apache/incubator-answer/internal/service/badge"
+       "github.com/apache/incubator-answer/internal/service/badge_award"
+       "github.com/apache/incubator-answer/internal/service/badge_group"
        "github.com/apache/incubator-answer/internal/service/collection"
        collectioncommon 
"github.com/apache/incubator-answer/internal/service/collection_common"
        "github.com/apache/incubator-answer/internal/service/comment"
@@ -117,4 +120,7 @@ var ProviderSetService = wire.NewSet(
        notice_queue.NewNewQuestionNotificationQueueService,
        review.NewReviewService,
        meta.NewMetaService,
+       badge.NewBadgeService,
+       badge_award.NewBadgeAwardService,
+       badge_group.NewBadgeGroupService,
 )

Reply via email to