You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by ro...@apache.org on 2024/08/21 07:13:42 UTC

(incubator-answer) 41/49: feat(badge): award badge for user

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 e2e8df46550b0abfbb19849ed1c8f1b5e94f2744
Author: LinkinStars <li...@foxmail.com>
AuthorDate: Wed Aug 14 10:37:37 2024 +0800

    feat(badge): award badge for user
---
 cmd/wire_gen.go                               |  4 +-
 docs/docs.go                                  | 19 ++++++
 internal/entity/badge_award_entity.go         | 16 ++---
 internal/repo/badge/badge_event_rule.go       |  2 +-
 internal/repo/badge_award/badge_award_repo.go | 88 +++++++++++++++++++--------
 internal/service/badge/badge_award_service.go | 47 +++++---------
 internal/service/badge/badge_event_handler.go | 34 ++++++-----
 7 files changed, 129 insertions(+), 81 deletions(-)

diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go
index d4034b6b..9acd76ee 100644
--- a/cmd/wire_gen.go
+++ b/cmd/wire_gen.go
@@ -263,9 +263,9 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
 	badgeGroupRepo := badge_group.NewBadgeGroupRepo(dataData, uniqueIDRepo)
 	badgeAwardRepo := badge_award.NewBadgeAwardRepo(dataData, uniqueIDRepo)
 	eventRuleRepo := badge.NewEventRuleRepo(dataData)
-	badgeEventService := badge2.NewBadgeEventService(dataData, eventQueueService, badgeRepo, eventRuleRepo)
-	badgeService := badge2.NewBadgeService(badgeRepo, badgeGroupRepo, badgeAwardRepo, badgeEventService)
 	badgeAwardService := badge2.NewBadgeAwardService(badgeAwardRepo, badgeRepo, userCommon, objService)
+	badgeEventService := badge2.NewBadgeEventService(dataData, eventQueueService, badgeRepo, eventRuleRepo, badgeAwardService)
+	badgeService := badge2.NewBadgeService(badgeRepo, badgeGroupRepo, badgeAwardRepo, badgeEventService)
 	badgeController := controller.NewBadgeController(badgeService, badgeAwardService)
 	controller_adminBadgeController := controller_admin.NewBadgeController(badgeService)
 	answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, userAdminController, reasonController, themeController, siteInfoController, controllerSiteInfoController, notificationController, dashboardController, uploadController, activityController, roleController, pluginCon [...]
diff --git a/docs/docs.go b/docs/docs.go
index 7fef400f..cfefe0ed 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -1,3 +1,22 @@
+/*
+ * 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 docs Code generated by swaggo/swag. DO NOT EDIT
 package docs
 
diff --git a/internal/entity/badge_award_entity.go b/internal/entity/badge_award_entity.go
index 0eb30227..5ba475cd 100644
--- a/internal/entity/badge_award_entity.go
+++ b/internal/entity/badge_award_entity.go
@@ -24,18 +24,20 @@ import "time"
 const (
 	IsBadgeNotDeleted = 0
 	IsBadgeDeleted    = 1
+
+	BadgeOnceAwardKey = "0"
 )
 
 // BadgeAward badge_award
 type BadgeAward struct {
 	ID             string    `xorm:"not null pk BIGINT(20) id"`
-	CreatedAt      time.Time `json:"created_at" xorm:"created not null default CURRENT_TIMESTAMP TIMESTAMP created_at"`
-	UpdatedAt      time.Time `json:"updated_at" xorm:"updated not null default CURRENT_TIMESTAMP TIMESTAMP updated_at"`
-	UserID         string    `json:"user_id" xorm:"not null index BIGINT(20) user_id"`
-	BadgeID        string    `json:"badge_id" xorm:"not null index BIGINT(20) badge_id"`
-	AwardKey       string    `json:"award_key" xorm:"not null index VARCHAR(64) award_key"`
-	BadgeGroupID   int64     `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) is_badge_deleted"`
+	CreatedAt      time.Time `xorm:"created not null default CURRENT_TIMESTAMP TIMESTAMP created_at"`
+	UpdatedAt      time.Time `xorm:"updated not null default CURRENT_TIMESTAMP TIMESTAMP updated_at"`
+	UserID         string    `xorm:"not null index BIGINT(20) user_id"`
+	BadgeID        string    `xorm:"not null index BIGINT(20) badge_id"`
+	AwardKey       string    `xorm:"not null index VARCHAR(64) award_key"`
+	BadgeGroupID   int64     `xorm:"not null index BIGINT(20) badge_group_id"`
+	IsBadgeDeleted int8      `xorm:"not null TINYINT(1) is_badge_deleted"`
 }
 
 // TableName badge_award table name
diff --git a/internal/repo/badge/badge_event_rule.go b/internal/repo/badge/badge_event_rule.go
index 41d56686..f912c2bb 100644
--- a/internal/repo/badge/badge_event_rule.go
+++ b/internal/repo/badge/badge_event_rule.go
@@ -99,7 +99,7 @@ func (br *eventRuleRepo) FirstUpdateUserProfile(ctx context.Context,
 		return nil, nil
 	}
 	if len(bean.Bio) > 0 {
-		return append(awards, br.createBadgeAward(event.UserID, b.ID, "")), nil
+		return append(awards, br.createBadgeAward(event.UserID, b.ID, entity.BadgeOnceAwardKey)), nil
 	}
 	return nil, nil
 }
diff --git a/internal/repo/badge_award/badge_award_repo.go b/internal/repo/badge_award/badge_award_repo.go
index 5a5d56cd..058fa864 100644
--- a/internal/repo/badge_award/badge_award_repo.go
+++ b/internal/repo/badge_award/badge_award_repo.go
@@ -21,6 +21,7 @@ package badge_award
 
 import (
 	"context"
+	"fmt"
 	"github.com/apache/incubator-answer/internal/base/data"
 	"github.com/apache/incubator-answer/internal/base/pager"
 	"github.com/apache/incubator-answer/internal/base/reason"
@@ -28,6 +29,7 @@ import (
 	"github.com/apache/incubator-answer/internal/service/badge"
 	"github.com/apache/incubator-answer/internal/service/unique"
 	"github.com/segmentfault/pacman/errors"
+	"xorm.io/xorm"
 )
 
 type badgeAwardRepo struct {
@@ -42,38 +44,62 @@ func NewBadgeAwardRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo) badge.
 	}
 }
 
-func (r *badgeAwardRepo) Add(ctx context.Context, badgeAward *entity.BadgeAward) (err error) {
+// AwardBadgeForUser award badge for user
+func (r *badgeAwardRepo) AwardBadgeForUser(ctx context.Context, badgeAward *entity.BadgeAward) (err error) {
 	badgeAward.ID, err = r.uniqueIDRepo.GenUniqueIDStr(ctx, entity.BadgeAward{}.TableName())
 	if err != nil {
-		return
+		return err
 	}
-	_, err = r.data.DB.Context(ctx).Insert(badgeAward)
-	return
-}
-func (r *badgeAwardRepo) CheckIsAward(ctx context.Context, badgeID string, userID string, awardKey string, singleOrMulti int8) (isAward bool) {
-	isAward = false
-	if singleOrMulti == entity.BadgeSingleAward {
-		_, exists, err := r.GetByUserIdAndBadgeId(ctx, userID, badgeID)
+
+	_, err = r.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
+		session = session.Context(ctx)
+
+		badgeInfo := &entity.Badge{}
+		exist, err := session.ID(badgeAward.BadgeID).ForUpdate().Get(badgeInfo)
 		if err != nil {
-			err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
-			return
+			return nil, err
 		}
-		if exists {
-			return true
+		if !exist {
+			return nil, fmt.Errorf("badge not exist")
 		}
-	} else {
-		_, exists, err := r.GetByUserIdAndBadgeIdAndObjectId(ctx, userID, badgeID, awardKey)
+
+		old := &entity.BadgeAward{}
+		exist, err = session.Where("user_id = ? AND badge_id = ? AND award_key = ? AND is_badge_deleted = 0",
+			badgeAward.UserID, badgeAward.BadgeID, badgeAward.AwardKey).Get(old)
 		if err != nil {
-			err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
-			return
+			return nil, err
 		}
-		if exists {
-			return true
+		if exist {
+			return nil, fmt.Errorf("badge already awarded")
 		}
+
+		_, err = session.Insert(badgeAward)
+		if err != nil {
+			return nil, err
+		}
+
+		return session.ID(badgeInfo.ID).Incr("award_count", 1).Update(&entity.Badge{})
+	})
+	if err != nil {
+		return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
 	}
+	return nil
+}
 
-	return
+// CheckIsAward check this badge is awarded for this user or not
+func (r *badgeAwardRepo) CheckIsAward(ctx context.Context, badgeID, userID, awardKey string, singleOrMulti int8) (
+	isAward bool, err error) {
+	if singleOrMulti == entity.BadgeSingleAward {
+		_, isAward, err = r.GetByUserIdAndBadgeId(ctx, userID, badgeID)
+	} else {
+		_, isAward, err = r.GetByUserIdAndBadgeIdAndAwardKey(ctx, userID, badgeID, awardKey)
+	}
+	if err != nil {
+		err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+	}
+	return isAward, err
 }
+
 func (r *badgeAwardRepo) CountByUserIdAndBadgeLevel(ctx context.Context, userID string, badgeLevel entity.BadgeLevel) (awardCount int64) {
 	return
 }
@@ -161,13 +187,25 @@ func (r *badgeAwardRepo) ListNewestByUserIdAndLevel(ctx context.Context, userID
 }
 
 // GetByUserIdAndBadgeId get badge award by user id and badge id
-func (r *badgeAwardRepo) GetByUserIdAndBadgeId(ctx context.Context, userID string, badgeID string) (badgeAward *entity.BadgeAward, exists bool, err error) {
-	exists, err = r.data.DB.Context(ctx).Where("user_id = ? AND badge_id = ? AND is_badge_deleted = 0", userID, badgeID).Get(&badgeAward)
+func (r *badgeAwardRepo) GetByUserIdAndBadgeId(ctx context.Context, userID string, badgeID string) (
+	badgeAward *entity.BadgeAward, exists bool, err error) {
+	badgeAward = &entity.BadgeAward{}
+	exists, err = r.data.DB.Context(ctx).
+		Where("user_id = ? AND badge_id = ? AND is_badge_deleted = 0", userID, badgeID).Get(badgeAward)
+	if err != nil {
+		err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+	}
 	return
 }
 
-// GetByUserIdAndBadgeIdAndObjectId get badge award by user id, badge id and object id
-func (r *badgeAwardRepo) GetByUserIdAndBadgeIdAndObjectId(ctx context.Context, userID string, badgeID string, awardKey string) (badgeAward *entity.BadgeAward, exists bool, err error) {
-	exists, err = r.data.DB.Context(ctx).Where("user_id = ? AND badge_id = ? AND award_key = ? AND is_badge_deleted = 0", userID, badgeID, awardKey).Get(&badgeAward)
+// GetByUserIdAndBadgeIdAndAwardKey get badge award by user id and badge id and award key
+func (r *badgeAwardRepo) GetByUserIdAndBadgeIdAndAwardKey(ctx context.Context, userID string, badgeID string, awardKey string) (
+	badgeAward *entity.BadgeAward, exists bool, err error) {
+	badgeAward = &entity.BadgeAward{}
+	exists, err = r.data.DB.Context(ctx).
+		Where("user_id = ? AND badge_id = ? AND award_key = ? AND is_badge_deleted = 0", userID, badgeID, awardKey).Get(badgeAward)
+	if err != nil {
+		err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+	}
 	return
 }
diff --git a/internal/service/badge/badge_award_service.go b/internal/service/badge/badge_award_service.go
index 96a9743f..3883de61 100644
--- a/internal/service/badge/badge_award_service.go
+++ b/internal/service/badge/badge_award_service.go
@@ -33,12 +33,11 @@ import (
 	"github.com/jinzhu/copier"
 	"github.com/segmentfault/pacman/errors"
 	"github.com/segmentfault/pacman/log"
-	"time"
 )
 
 type BadgeAwardRepo interface {
-	Add(ctx context.Context, badgeAward *entity.BadgeAward) (err error)
-	CheckIsAward(ctx context.Context, badgeID string, userID string, awardKey string, singleOrMulti int8) bool
+	CheckIsAward(ctx context.Context, badgeID string, userID string, awardKey string, singleOrMulti int8) (isAward bool, err error)
+	AwardBadgeForUser(ctx context.Context, badgeAward *entity.BadgeAward) (err error)
 
 	CountByUserIdAndBadgeLevel(ctx context.Context, userID string, badgeLevel entity.BadgeLevel) (awardCount int64)
 	CountByUserId(ctx context.Context, userID string) (awardCount int64)
@@ -62,7 +61,7 @@ type BadgeAwardRepo interface {
 	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, exists bool, err error)
-	GetByUserIdAndBadgeIdAndObjectId(ctx context.Context, userID string, badgeID string, awardKey string) (badgeAward *entity.BadgeAward, exists bool, err error)
+	GetByUserIdAndBadgeIdAndAwardKey(ctx context.Context, userID string, badgeID string, awardKey string) (badgeAward *entity.BadgeAward, exists bool, err error)
 }
 
 type BadgeAwardService struct {
@@ -151,48 +150,32 @@ func (b *BadgeAwardService) GetBadgeAwardList(
 }
 
 // Award award badge
-func (b *BadgeAwardService) Award(ctx context.Context, badgeID string, userID string, awardKey string, force bool, createdAt time.Time) (err error) {
-	var (
-		badgeData       *entity.Badge
-		exists, awarded bool
-	)
-
-	badgeData, exists, err = b.badgeRepo.GetByID(ctx, badgeID)
+func (b *BadgeAwardService) Award(ctx context.Context, badgeID string, userID string, awardKey string) (err error) {
+	badgeData, exists, err := b.badgeRepo.GetByID(ctx, badgeID)
 	if err != nil {
-		return
+		return err
 	}
 
 	if !exists || badgeData.Status == entity.BadgeStatusInactive {
-		err = errors.BadRequest(reason.BadgeObjectNotFound)
-		return
+		return errors.BadRequest(reason.BadgeObjectNotFound)
 	}
 
-	awarded = b.badgeAwardRepo.CheckIsAward(ctx, badgeID, userID, awardKey, badgeData.Single)
-	if !force && awarded {
-		return
+	alreadyAwarded, err := b.badgeAwardRepo.CheckIsAward(ctx, badgeID, userID, awardKey, badgeData.Single)
+	if err != nil {
+		return err
 	}
-
-	if createdAt.IsZero() {
-		createdAt = time.Now()
+	if alreadyAwarded {
+		return nil
 	}
 
-	err = b.badgeAwardRepo.Add(ctx, &entity.BadgeAward{
-		CreatedAt:      createdAt,
-		UpdatedAt:      createdAt,
+	badgeAward := &entity.BadgeAward{
 		UserID:         userID,
 		BadgeID:        badgeID,
 		AwardKey:       awardKey,
 		BadgeGroupID:   badgeData.BadgeGroupID,
-		IsBadgeDeleted: 0,
-	})
-	if err != nil {
-		return
+		IsBadgeDeleted: entity.IsBadgeNotDeleted,
 	}
-
-	// increment badge award count
-	err = b.badgeRepo.UpdateAwardCount(ctx, badgeID, 1)
-
-	return
+	return b.badgeAwardRepo.AwardBadgeForUser(ctx, badgeAward)
 }
 
 // GetUserBadgeAwardList get user badge award list
diff --git a/internal/service/badge/badge_event_handler.go b/internal/service/badge/badge_event_handler.go
index 09331197..f990260b 100644
--- a/internal/service/badge/badge_event_handler.go
+++ b/internal/service/badge/badge_event_handler.go
@@ -21,12 +21,11 @@ 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/schema"
 	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"github.com/segmentfault/pacman/log"
-
-	"github.com/apache/incubator-answer/internal/base/data"
-	"github.com/apache/incubator-answer/internal/schema"
 )
 
 type BadgeEventService struct {
@@ -35,6 +34,7 @@ type BadgeEventService struct {
 	badgeAwardRepo    BadgeAwardRepo
 	badgeRepo         BadgeRepo
 	eventRuleRepo     EventRuleRepo
+	badgeAwardService *BadgeAwardService
 }
 
 type EventRuleHandler func(ctx context.Context, event *schema.EventMsg) (awards []*entity.BadgeAward, err error)
@@ -48,12 +48,14 @@ func NewBadgeEventService(
 	eventQueueService event_queue.EventQueueService,
 	badgeRepo BadgeRepo,
 	eventRuleRepo EventRuleRepo,
+	badgeAwardService *BadgeAwardService,
 ) *BadgeEventService {
 	n := &BadgeEventService{
 		data:              data,
 		eventQueueService: eventQueueService,
 		badgeRepo:         badgeRepo,
 		eventRuleRepo:     eventRuleRepo,
+		badgeAwardService: badgeAwardService,
 	}
 	eventQueueService.RegisterHandler(n.Handler)
 	return n
@@ -65,18 +67,22 @@ func (ns *BadgeEventService) Handler(ctx context.Context, msg *schema.EventMsg)
 		return nil
 	}
 
-	badgeIDs := make([]string, 0)
-	for _, award := range awards {
-		badgeIDs = append(badgeIDs, award.BadgeID)
-	}
+	//badgeIDs := make([]string, 0)
+	//for _, award := range awards {
+	//	badgeIDs = append(badgeIDs, award.BadgeID)
+	//}
+	//
+	//badges, err := ns.badgeRepo.GetByIDs(ctx, badgeIDs)
+	//if err != nil {
+	//	log.Errorf("error getting badges %+v: %v", badgeIDs, err)
+	//	return err
+	//}
 
-	badges, err := ns.badgeRepo.GetByIDs(ctx, badgeIDs)
-	if err != nil {
-		log.Errorf("error getting badges %+v: %v", badgeIDs, err)
-		return err
+	for _, award := range awards {
+		err := ns.badgeAwardService.Award(ctx, award.BadgeID, award.UserID, award.AwardKey)
+		if err != nil {
+			log.Debugf("error awarding badge %s: %v", award.BadgeID, err)
+		}
 	}
-
-	// TODO: award badges to user
-	log.Debugf("awarding badges %+v to user", badges)
 	return nil
 }