This is an automated email from the ASF dual-hosted git repository.

robin0716 pushed a commit to branch develop/robin
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git

commit ad1e0aa075cdf1d61b9cdb0cf95fdd0650ae1179
Author: kumfo <ku...@sifou.com>
AuthorDate: Thu Aug 8 16:41:17 2024 +0800

    feat(badge): badge info && badge award list
---
 cmd/wire_gen.go                                    |   4 +-
 docs/docs.go                                       | 156 +++++++++++++++++++++
 docs/swagger.json                                  | 156 +++++++++++++++++++++
 docs/swagger.yaml                                  |  96 +++++++++++++
 i18n/en_US.yaml                                    |   3 +
 internal/base/reason/reason.go                     |   1 +
 internal/controller/badge_controller.go            |  61 +++++++-
 internal/entity/badge_award_entity.go              |   8 +-
 internal/repo/badge/badge_repo.go                  |   6 +
 internal/repo/badge_award/badge_award_repo.go      |  33 +++--
 internal/router/answer_api_router.go               |   2 +
 internal/schema/badge.go                           |  34 -----
 internal/schema/badge_schema.go                    |  70 +++++++++
 internal/service/badge/badge_service.go            |  52 ++++++-
 .../service/badge_award/badge_award_service.go     | 102 ++++++++++++--
 15 files changed, 718 insertions(+), 66 deletions(-)

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


Reply via email to