This is an automated email from the ASF dual-hosted git repository.
linkinstar 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 7d040f07 feat(badge): add badge notification
7d040f07 is described below
commit 7d040f07dd8faf244eab2695036f69342e7e3b01
Author: LinkinStars <[email protected]>
AuthorDate: Thu Aug 15 18:34:13 2024 +0800
feat(badge): add badge notification
---
cmd/wire_gen.go | 6 +-
i18n/en_US.yaml | 2 +
internal/base/constant/cache_key.go | 2 +
internal/base/constant/notification.go | 8 ++
internal/controller/notification_controller.go | 6 +-
internal/repo/notification/notification_repo.go | 6 +-
internal/schema/badge_schema.go | 18 ++++
internal/schema/notification_schema.go | 76 ++++++++++++++--
internal/service/badge/badge_award_service.go | 85 ++++++++++--------
.../service/notification/notification_service.go | 100 ++++++++++++++-------
.../service/notification_common/notification.go | 89 ++++++++++++++----
internal/service/object_info/object_info.go | 1 -
12 files changed, 301 insertions(+), 98 deletions(-)
diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go
index 9acd76ee..cc245aea 100644
--- a/cmd/wire_gen.go
+++ b/cmd/wire_gen.go
@@ -238,7 +238,8 @@ func initApplication(debug bool, serverConf *conf.Server,
dbConf *data.Database,
controllerSiteInfoController :=
controller.NewSiteInfoController(siteInfoCommonService)
notificationRepo := notification2.NewNotificationRepo(dataData)
notificationCommon :=
notificationcommon.NewNotificationCommon(dataData, notificationRepo,
userCommon, activityRepo, followRepo, objService, notificationQueueService,
userExternalLoginRepo, siteInfoCommonService)
- notificationService := notification.NewNotificationService(dataData,
notificationRepo, notificationCommon, revisionService, userRepo, reportRepo,
reviewService)
+ badgeRepo := badge.NewBadgeRepo(dataData, uniqueIDRepo)
+ notificationService := notification.NewNotificationService(dataData,
notificationRepo, notificationCommon, revisionService, userRepo, reportRepo,
reviewService, badgeRepo)
notificationController :=
controller.NewNotificationController(notificationService, rankService)
dashboardService := dashboard.NewDashboardService(questionRepo,
answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configService,
siteInfoCommonService, serviceConf, reviewService, revisionRepo, dataData)
dashboardController :=
controller.NewDashboardController(dashboardService)
@@ -259,11 +260,10 @@ func initApplication(debug bool, serverConf *conf.Server,
dbConf *data.Database,
reviewController := controller.NewReviewController(reviewService,
rankService, captchaService)
metaService := meta2.NewMetaService(metaCommonService, userCommon,
answerRepo, questionRepo, eventQueueService)
metaController := controller.NewMetaController(metaService)
- badgeRepo := badge.NewBadgeRepo(dataData, uniqueIDRepo)
badgeGroupRepo := badge_group.NewBadgeGroupRepo(dataData, uniqueIDRepo)
badgeAwardRepo := badge_award.NewBadgeAwardRepo(dataData, uniqueIDRepo)
eventRuleRepo := badge.NewEventRuleRepo(dataData)
- badgeAwardService := badge2.NewBadgeAwardService(badgeAwardRepo,
badgeRepo, userCommon, objService)
+ badgeAwardService := badge2.NewBadgeAwardService(badgeAwardRepo,
badgeRepo, userCommon, objService, notificationQueueService)
badgeEventService := badge2.NewBadgeEventService(dataData,
eventQueueService, badgeRepo, eventRuleRepo, badgeAwardService)
badgeService := badge2.NewBadgeService(badgeRepo, badgeGroupRepo,
badgeAwardRepo, badgeEventService)
badgeController := controller.NewBadgeController(badgeService,
badgeAwardService)
diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 071644f4..4de69658 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -465,6 +465,8 @@ backend:
other: upvoted comment
invited_you_to_answer:
other: invited you to answer
+ earned_badge:
+ other: You've earned the "{{.BadgeName}}" badge
email_tpl:
change_email:
title:
diff --git a/internal/base/constant/cache_key.go
b/internal/base/constant/cache_key.go
index 4135b53c..987798d1 100644
--- a/internal/base/constant/cache_key.go
+++ b/internal/base/constant/cache_key.go
@@ -50,4 +50,6 @@ const (
NewQuestionNotificationLimitMax = 50
RateLimitCacheKeyPrefix = "answer:rate-limit:"
RateLimitCacheTime = 5 * time.Minute
+ RedDotCacheKey = "answer:red-dot:%s:%s"
+ RedDotCacheTime = 30 * 24 * time.Hour
)
diff --git a/internal/base/constant/notification.go
b/internal/base/constant/notification.go
index ceebe7de..9a7762d8 100644
--- a/internal/base/constant/notification.go
+++ b/internal/base/constant/notification.go
@@ -56,6 +56,8 @@ const (
NotificationYourCommentWasDeleted =
"notification.action.your_comment_was_deleted"
// NotificationInvitedYouToAnswer invited you to answer
NotificationInvitedYouToAnswer =
"notification.action.invited_you_to_answer"
+ // NotificationEarnedBadge earned badge
+ NotificationEarnedBadge = "notification.action.earned_badge"
)
type NotificationChannelKey string
@@ -71,6 +73,12 @@ const (
EmailChannel NotificationChannelKey = "email"
)
+const (
+ NotificationTypeInbox = "inbox"
+ NotificationTypeAchievement = "achievement"
+ NotificationTypeBadgeAchievement = "badge"
+)
+
var (
NotificationMsgTypeMapping = map[string]int{
NotificationUpdateQuestion: 1,
diff --git a/internal/controller/notification_controller.go
b/internal/controller/notification_controller.go
index 15796b9c..952c262e 100644
--- a/internal/controller/notification_controller.go
+++ b/internal/controller/notification_controller.go
@@ -105,8 +105,8 @@ func (nc *NotificationController) ClearRedDot(ctx
*gin.Context) {
req.CanReviewAnswer = canList[1]
req.CanReviewTag = canList[2]
- RedDot, err := nc.notificationService.ClearRedDot(ctx, req)
- handler.HandleResponse(ctx, err, RedDot)
+ resp, err := nc.notificationService.ClearRedDot(ctx, req)
+ handler.HandleResponse(ctx, err, resp)
}
// ClearUnRead
@@ -125,7 +125,7 @@ func (nc *NotificationController) ClearUnRead(ctx
*gin.Context) {
return
}
userID := middleware.GetLoginUserIDFromContext(ctx)
- err := nc.notificationService.ClearUnRead(ctx, userID, req.TypeStr)
+ err := nc.notificationService.ClearUnRead(ctx, userID,
req.NotificationType)
handler.HandleResponse(ctx, err, gin.H{})
}
diff --git a/internal/repo/notification/notification_repo.go
b/internal/repo/notification/notification_repo.go
index 6b4f0040..bd325ef2 100644
--- a/internal/repo/notification/notification_repo.go
+++ b/internal/repo/notification/notification_repo.go
@@ -69,7 +69,7 @@ func (nr *notificationRepo) UpdateNotificationContent(ctx
context.Context, notif
func (nr *notificationRepo) ClearUnRead(ctx context.Context, userID string,
notificationType int) (err error) {
info := &entity.Notification{}
info.IsRead = schema.NotificationRead
- _, err = nr.data.DB.Context(ctx).Where("user_id =?", userID).And("type
=?", notificationType).Cols("is_read").Update(info)
+ _, err = nr.data.DB.Context(ctx).Where("user_id = ?", userID).And("type
= ?", notificationType).Cols("is_read").Update(info)
if err != nil {
return
errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@@ -79,7 +79,7 @@ func (nr *notificationRepo) ClearUnRead(ctx context.Context,
userID string, noti
func (nr *notificationRepo) ClearIDUnRead(ctx context.Context, userID string,
id string) (err error) {
info := &entity.Notification{}
info.IsRead = schema.NotificationRead
- _, err = nr.data.DB.Context(ctx).Where("user_id =?", userID).And("id
=?", id).Cols("is_read").Update(info)
+ _, err = nr.data.DB.Context(ctx).Where("user_id = ?", userID).And("id =
?", id).Cols("is_read").Update(info)
if err != nil {
return
errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
@@ -98,7 +98,7 @@ func (nr *notificationRepo) GetById(ctx context.Context, id
string) (*entity.Not
func (nr *notificationRepo) GetByUserIdObjectIdTypeId(ctx context.Context,
userID, objectID string, notificationType int) (*entity.Notification, bool,
error) {
info := &entity.Notification{}
- exist, err := nr.data.DB.Context(ctx).Where("user_id = ? ",
userID).And("object_id = ?", objectID).And("type = ?",
notificationType).Get(info)
+ exist, err := nr.data.DB.Context(ctx).Where("user_id = ?",
userID).And("object_id = ?", objectID).And("type = ?",
notificationType).Get(info)
if err != nil {
err =
errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return info, false, err
diff --git a/internal/schema/badge_schema.go b/internal/schema/badge_schema.go
index 088193ad..efbcd37a 100644
--- a/internal/schema/badge_schema.go
+++ b/internal/schema/badge_schema.go
@@ -171,3 +171,21 @@ type GetUserBadgeAwardListResp struct {
// badge level
Level entity.BadgeLevel `json:"level" `
}
+
+// GetBadgeByIDResp get badge by id response
+type GetBadgeByIDResp struct {
+ // badge id
+ ID string `json:"id" `
+ // badge name
+ Name string `json:"name" `
+ // badge description
+ Description string `json:"description" `
+ // badge icon
+ Icon string `json:"icon" `
+ // badge award count
+ AwardCount int `json:"award_count" `
+ // badge is single or multiple
+ IsSingle bool `json:"is_single" `
+ // badge level
+ Level entity.BadgeLevel `json:"level" `
+}
diff --git a/internal/schema/notification_schema.go
b/internal/schema/notification_schema.go
index 4e0e9316..8d4b694e 100644
--- a/internal/schema/notification_schema.go
+++ b/internal/schema/notification_schema.go
@@ -19,6 +19,12 @@
package schema
+import (
+ "encoding/json"
+ "github.com/apache/incubator-answer/internal/entity"
+ "sort"
+)
+
const (
NotificationTypeInbox = 1
NotificationTypeAchievement = 2
@@ -95,10 +101,70 @@ type ObjectInfo struct {
}
type RedDot struct {
- Inbox int64 `json:"inbox"`
- Achievement int64 `json:"achievement"`
- Revision int64 `json:"revision"`
- CanRevision bool `json:"can_revision"`
+ Inbox int64 `json:"inbox"`
+ Achievement int64 `json:"achievement"`
+ Revision int64 `json:"revision"`
+ CanRevision bool `json:"can_revision"`
+ BadgeAward *RedDotBadgeAward `json:"badge_award"`
+}
+
+type RedDotBadgeAward struct {
+ NotificationID string `json:"notification_id"`
+ BadgeID string `json:"badge_id"`
+ Name string `json:"name"`
+ Icon string `json:"icon"`
+ Level entity.BadgeLevel `json:"level"`
+}
+
+type RedDotBadgeAwardCache struct {
+ BadgeAwardList map[string]*RedDotBadgeAward `json:"badge_award_list"`
+}
+
+// NewRedDotBadgeAwardCache new red dot badge award cache
+func NewRedDotBadgeAwardCache() *RedDotBadgeAwardCache {
+ return &RedDotBadgeAwardCache{
+ BadgeAwardList: make(map[string]*RedDotBadgeAward),
+ }
+}
+
+// GetBadgeAward get badge award
+func (r *RedDotBadgeAwardCache) GetBadgeAward() *RedDotBadgeAward {
+ if len(r.BadgeAwardList) == 0 {
+ return nil
+ }
+ var ids []string
+ for _, v := range r.BadgeAwardList {
+ ids = append(ids, v.NotificationID)
+ }
+ sort.Strings(ids)
+ return r.BadgeAwardList[ids[0]]
+}
+
+// FromJSON from json
+func (r *RedDotBadgeAwardCache) FromJSON(data string) {
+ _ = json.Unmarshal([]byte(data), r)
+}
+
+// ToJSON to json
+func (r *RedDotBadgeAwardCache) ToJSON() string {
+ data, _ := json.Marshal(r)
+ return string(data)
+}
+
+// AddBadgeAward add badge award
+func (r *RedDotBadgeAwardCache) AddBadgeAward(badgeAward *RedDotBadgeAward) {
+ if r.BadgeAwardList == nil {
+ r.BadgeAwardList = make(map[string]*RedDotBadgeAward)
+ }
+ r.BadgeAwardList[badgeAward.NotificationID] = badgeAward
+}
+
+// RemoveBadgeAward remove badge award
+func (r *RedDotBadgeAwardCache) RemoveBadgeAward(notificationID string) {
+ if r.BadgeAwardList == nil {
+ return
+ }
+ delete(r.BadgeAwardList, notificationID)
}
type NotificationSearch struct {
@@ -112,8 +178,8 @@ type NotificationSearch struct {
}
type NotificationClearRequest struct {
+ NotificationType string `validate:"required,oneof=inbox achievement"
json:"type"`
UserID string `json:"-"`
- TypeStr string `json:"type" form:"type"` // inbox achievement
CanReviewQuestion bool `json:"-"`
CanReviewAnswer bool `json:"-"`
CanReviewTag bool `json:"-"`
diff --git a/internal/service/badge/badge_award_service.go
b/internal/service/badge/badge_award_service.go
index 3883de61..dd78fd02 100644
--- a/internal/service/badge/badge_award_service.go
+++ b/internal/service/badge/badge_award_service.go
@@ -21,11 +21,13 @@ package badge
import (
"context"
+ "github.com/apache/incubator-answer/internal/base/constant"
"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/notice_queue"
"github.com/apache/incubator-answer/internal/service/object_info"
usercommon
"github.com/apache/incubator-answer/internal/service/user_common"
"github.com/apache/incubator-answer/pkg/uid"
@@ -65,10 +67,11 @@ type BadgeAwardRepo interface {
}
type BadgeAwardService struct {
- badgeAwardRepo BadgeAwardRepo
- badgeRepo BadgeRepo
- userCommon *usercommon.UserCommon
- objectInfoService *object_info.ObjService
+ badgeAwardRepo BadgeAwardRepo
+ badgeRepo BadgeRepo
+ userCommon *usercommon.UserCommon
+ objectInfoService *object_info.ObjService
+ notificationQueueService notice_queue.NotificationQueueService
}
func NewBadgeAwardService(
@@ -76,17 +79,19 @@ func NewBadgeAwardService(
badgeRepo BadgeRepo,
userCommon *usercommon.UserCommon,
objectInfoService *object_info.ObjService,
+ notificationQueueService notice_queue.NotificationQueueService,
) *BadgeAwardService {
return &BadgeAwardService{
- badgeAwardRepo: badgeAwardRepo,
- badgeRepo: badgeRepo,
- userCommon: userCommon,
- objectInfoService: objectInfoService,
+ badgeAwardRepo: badgeAwardRepo,
+ badgeRepo: badgeRepo,
+ userCommon: userCommon,
+ objectInfoService: objectInfoService,
+ notificationQueueService: notificationQueueService,
}
}
// GetBadgeAwardList get badge award list
-func (b *BadgeAwardService) GetBadgeAwardList(
+func (bs *BadgeAwardService) GetBadgeAwardList(
ctx context.Context,
req *schema.GetBadgeAwardWithPageReq,
) (resp []*schema.GetBadgeAwardWithPageResp, total int64, err error) {
@@ -94,11 +99,11 @@ func (b *BadgeAwardService) GetBadgeAwardList(
badgeAwardList []*entity.BadgeAward
)
- req.UserID, err = b.validateUserByUsername(ctx, req.Username)
+ req.UserID, err = bs.validateUserByUsername(ctx, req.Username)
if err != nil {
- badgeAwardList, total, err =
b.badgeAwardRepo.ListPagedByBadgeId(ctx, req.BadgeID, req.Page, req.PageSize)
+ badgeAwardList, total, err =
bs.badgeAwardRepo.ListPagedByBadgeId(ctx, req.BadgeID, req.Page, req.PageSize)
} else {
- badgeAwardList, total, err =
b.badgeAwardRepo.ListPagedByBadgeIdAndUserId(ctx, req.BadgeID, req.UserID,
req.Page, req.PageSize)
+ badgeAwardList, total, err =
bs.badgeAwardRepo.ListPagedByBadgeIdAndUserId(ctx, req.BadgeID, req.UserID,
req.Page, req.PageSize)
}
if err != nil {
@@ -113,7 +118,7 @@ func (b *BadgeAwardService) GetBadgeAwardList(
)
// if exist object info
- objInfo, e := b.objectInfoService.GetInfo(ctx,
badgeAward.AwardKey)
+ objInfo, e := bs.objectInfoService.GetInfo(ctx,
badgeAward.AwardKey)
if e == nil && !objInfo.IsDeleted() {
objectID = objInfo.ObjectID
questionID = objInfo.QuestionID
@@ -135,7 +140,7 @@ func (b *BadgeAwardService) GetBadgeAwardList(
}
// get user info
- userInfo, exists, e := b.userCommon.GetUserBasicInfoByID(ctx,
badgeAward.UserID)
+ userInfo, exists, e := bs.userCommon.GetUserBasicInfoByID(ctx,
badgeAward.UserID)
if e != nil {
log.Errorf("user not found by id: %s, err: %v",
badgeAward.UserID, e)
}
@@ -150,8 +155,8 @@ func (b *BadgeAwardService) GetBadgeAwardList(
}
// Award award badge
-func (b *BadgeAwardService) Award(ctx context.Context, badgeID string, userID
string, awardKey string) (err error) {
- badgeData, exists, err := b.badgeRepo.GetByID(ctx, badgeID)
+func (bs *BadgeAwardService) Award(ctx context.Context, badgeID string, userID
string, awardKey string) (err error) {
+ badgeData, exists, err := bs.badgeRepo.GetByID(ctx, badgeID)
if err != nil {
return err
}
@@ -160,7 +165,7 @@ func (b *BadgeAwardService) Award(ctx context.Context,
badgeID string, userID st
return errors.BadRequest(reason.BadgeObjectNotFound)
}
- alreadyAwarded, err := b.badgeAwardRepo.CheckIsAward(ctx, badgeID,
userID, awardKey, badgeData.Single)
+ alreadyAwarded, err := bs.badgeAwardRepo.CheckIsAward(ctx, badgeID,
userID, awardKey, badgeData.Single)
if err != nil {
return err
}
@@ -175,11 +180,27 @@ func (b *BadgeAwardService) Award(ctx context.Context,
badgeID string, userID st
BadgeGroupID: badgeData.BadgeGroupID,
IsBadgeDeleted: entity.IsBadgeNotDeleted,
}
- return b.badgeAwardRepo.AwardBadgeForUser(ctx, badgeAward)
+ err = bs.badgeAwardRepo.AwardBadgeForUser(ctx, badgeAward)
+ if err != nil {
+ return err
+ }
+
+ msg := &schema.NotificationMsg{
+ TriggerUserID: badgeAward.UserID,
+ ReceiverUserID: badgeAward.UserID,
+ Type: schema.NotificationTypeAchievement,
+ ObjectID: badgeAward.ID,
+ ObjectType: constant.BadgeAwardObjectType,
+ Title: badgeData.Name,
+ ExtraInfo: map[string]string{"badge_id": badgeData.ID},
+ NotificationAction: constant.NotificationEarnedBadge,
+ }
+ bs.notificationQueueService.Send(ctx, msg)
+ return nil
}
// GetUserBadgeAwardList get user badge award list
-func (b *BadgeAwardService) GetUserBadgeAwardList(
+func (bs *BadgeAwardService) GetUserBadgeAwardList(
ctx *gin.Context,
req *schema.GetUserBadgeAwardListReq,
) (
@@ -191,12 +212,12 @@ func (b *BadgeAwardService) GetUserBadgeAwardList(
earnedCounts []*entity.BadgeEarnedCount
)
- req.UserID, err = b.validateUserByUsername(ctx, req.Username)
+ req.UserID, err = bs.validateUserByUsername(ctx, req.Username)
if err != nil {
return
}
- earnedCounts, err = b.badgeAwardRepo.SumUserEarnedGroupByBadgeID(ctx,
req.UserID)
+ earnedCounts, err = bs.badgeAwardRepo.SumUserEarnedGroupByBadgeID(ctx,
req.UserID)
if err != nil {
return
}
@@ -204,7 +225,7 @@ func (b *BadgeAwardService) GetUserBadgeAwardList(
resp = make([]*schema.GetUserBadgeAwardListResp, total)
for i, earnedCount := range earnedCounts {
- badge, exists, e := b.badgeRepo.GetByID(ctx,
earnedCount.BadgeID)
+ badge, exists, e := bs.badgeRepo.GetByID(ctx,
earnedCount.BadgeID)
if e != nil {
err = e
return
@@ -225,24 +246,18 @@ func (b *BadgeAwardService) GetUserBadgeAwardList(
}
// GetUserRecentBadgeAwardList get user badge award list
-func (b *BadgeAwardService) GetUserRecentBadgeAwardList(
- ctx *gin.Context,
- req *schema.GetUserBadgeAwardListReq,
-) (
- resp []*schema.GetUserBadgeAwardListResp,
- total int64,
- err error,
-) {
+func (bs *BadgeAwardService) GetUserRecentBadgeAwardList(ctx *gin.Context, req
*schema.GetUserBadgeAwardListReq) (
+ resp []*schema.GetUserBadgeAwardListResp, total int64, err error) {
var (
earnedCounts []*entity.BadgeAwardRecent
)
- req.UserID, err = b.validateUserByUsername(ctx, req.Username)
+ req.UserID, err = bs.validateUserByUsername(ctx, req.Username)
if err != nil {
return
}
- earnedCounts, err = b.badgeAwardRepo.ListNewestEarned(ctx, req.UserID,
req.Limit)
+ earnedCounts, err = bs.badgeAwardRepo.ListNewestEarned(ctx, req.UserID,
req.Limit)
if err != nil {
return
}
@@ -251,7 +266,7 @@ func (b *BadgeAwardService) GetUserRecentBadgeAwardList(
resp = make([]*schema.GetUserBadgeAwardListResp, total)
for i, earnedCount := range earnedCounts {
- badge, exists, e := b.badgeRepo.GetByID(ctx,
earnedCount.BadgeID)
+ badge, exists, e := bs.badgeRepo.GetByID(ctx,
earnedCount.BadgeID)
if e != nil {
err = e
return
@@ -278,14 +293,14 @@ type userReq struct {
Username string
}
-func (b *BadgeAwardService) validateUserByUsername(ctx context.Context,
userName string) (userID string, err error) {
+func (bs *BadgeAwardService) validateUserByUsername(ctx context.Context,
userName string) (userID string, err error) {
var (
userInfo *schema.UserBasicInfo
exist bool
)
// validate user exists or not
if len(userName) > 0 {
- userInfo, exist, err =
b.userCommon.GetUserBasicInfoByUserName(ctx, userName)
+ userInfo, exist, err =
bs.userCommon.GetUserBasicInfoByUserName(ctx, userName)
if err != nil {
return
}
diff --git a/internal/service/notification/notification_service.go
b/internal/service/notification/notification_service.go
index 71febb67..b73d4fda 100644
--- a/internal/service/notification/notification_service.go
+++ b/internal/service/notification/notification_service.go
@@ -23,7 +23,7 @@ import (
"context"
"encoding/json"
"fmt"
-
+ "github.com/apache/incubator-answer/internal/service/badge"
"github.com/apache/incubator-answer/internal/service/report_common"
"github.com/apache/incubator-answer/internal/service/review"
usercommon
"github.com/apache/incubator-answer/internal/service/user_common"
@@ -52,6 +52,7 @@ type NotificationService struct {
reportRepo report_common.ReportRepo
reviewService *review.ReviewService
userRepo usercommon.UserRepo
+ badgeRepo badge.BadgeRepo
}
func NewNotificationService(
@@ -62,6 +63,7 @@ func NewNotificationService(
userRepo usercommon.UserRepo,
reportRepo report_common.ReportRepo,
reviewService *review.ReviewService,
+ badgeRepo badge.BadgeRepo,
) *NotificationService {
return &NotificationService{
data: data,
@@ -71,35 +73,60 @@ func NewNotificationService(
userRepo: userRepo,
reportRepo: reportRepo,
reviewService: reviewService,
+ badgeRepo: badgeRepo,
}
}
func (ns *NotificationService) GetRedDot(ctx context.Context, req
*schema.GetRedDot) (resp *schema.RedDot, err error) {
+ inboxKey := fmt.Sprintf(constant.RedDotCacheKey,
constant.NotificationTypeInbox, req.UserID)
+ achievementKey := fmt.Sprintf(constant.RedDotCacheKey,
constant.NotificationTypeAchievement, req.UserID)
+
redBot := &schema.RedDot{}
- inboxKey := fmt.Sprintf("answer_RedDot_%d_%s",
schema.NotificationTypeInbox, req.UserID)
- achievementKey := fmt.Sprintf("answer_RedDot_%d_%s",
schema.NotificationTypeAchievement, req.UserID)
- inboxValue, _, err := ns.data.Cache.GetInt64(ctx, inboxKey)
- if err != nil {
- redBot.Inbox = 0
- } else {
- redBot.Inbox = inboxValue
- }
- achievementValue, _, err := ns.data.Cache.GetInt64(ctx, achievementKey)
- if err != nil {
- redBot.Achievement = 0
- } else {
- redBot.Achievement = achievementValue
- }
- revisionCount := &schema.RevisionSearch{}
- _ = copier.Copy(revisionCount, req)
+ redBot.Inbox, _, err = ns.data.Cache.GetInt64(ctx, inboxKey)
+ redBot.Achievement, _, err = ns.data.Cache.GetInt64(ctx, achievementKey)
+
+ // get review amount
if req.CanReviewAnswer || req.CanReviewQuestion || req.CanReviewTag {
redBot.CanRevision = true
redBot.Revision = ns.countAllReviewAmount(ctx, req)
}
+ // get badge award
+ redBot.BadgeAward = ns.getBadgeAward(ctx, req.UserID)
return redBot, nil
}
+func (ns *NotificationService) getBadgeAward(ctx context.Context, userID
string) (badgeAward *schema.RedDotBadgeAward) {
+ key := fmt.Sprintf(constant.RedDotCacheKey,
constant.NotificationTypeBadgeAchievement, userID)
+ cacheData, exist, err := ns.data.Cache.GetString(ctx, key)
+ if err != nil {
+ log.Errorf("get badge award failed: %v", err)
+ return nil
+ }
+ if !exist {
+ return nil
+ }
+
+ c := schema.NewRedDotBadgeAwardCache()
+ c.FromJSON(cacheData)
+ award := c.GetBadgeAward()
+ if award == nil {
+ return nil
+ }
+ badgeInfo, exists, err := ns.badgeRepo.GetByID(ctx, award.BadgeID)
+ if err != nil {
+ log.Errorf("get badge info failed: %v", err)
+ return nil
+ }
+ if !exists {
+ return nil
+ }
+ award.Name = translator.Tr(handler.GetLangByCtx(ctx), badgeInfo.Name)
+ award.Icon = badgeInfo.Icon
+ award.Level = badgeInfo.Level
+ return award
+}
+
func (ns *NotificationService) countAllReviewAmount(ctx context.Context, req
*schema.GetRedDot) (amount int64) {
// get queue amount
if req.IsAdmin {
@@ -137,21 +164,16 @@ func (ns *NotificationService) countAllReviewAmount(ctx
context.Context, req *sc
}
func (ns *NotificationService) ClearRedDot(ctx context.Context, req
*schema.NotificationClearRequest) (*schema.RedDot, error) {
- botType, ok := schema.NotificationType[req.TypeStr]
- if ok {
- key := fmt.Sprintf("answer_RedDot_%d_%s", botType, req.UserID)
- err := ns.data.Cache.Del(ctx, key)
- if err != nil {
- log.Error("ClearRedDot del cache error", err.Error())
- }
- }
- getRedDotreq := &schema.GetRedDot{}
- _ = copier.Copy(getRedDotreq, req)
- return ns.GetRedDot(ctx, getRedDotreq)
+ key := fmt.Sprintf(constant.RedDotCacheKey, req.NotificationType,
req.UserID)
+ _ = ns.data.Cache.Del(ctx, key)
+
+ resp := &schema.GetRedDot{}
+ _ = copier.Copy(resp, req)
+ return ns.GetRedDot(ctx, resp)
}
-func (ns *NotificationService) ClearUnRead(ctx context.Context, userID string,
botTypeStr string) error {
- botType, ok := schema.NotificationType[botTypeStr]
+func (ns *NotificationService) ClearUnRead(ctx context.Context, userID string,
notificationType string) error {
+ botType, ok := schema.NotificationType[notificationType]
if ok {
err := ns.notificationRepo.ClearUnRead(ctx, userID, botType)
if err != nil {
@@ -164,19 +186,23 @@ func (ns *NotificationService) ClearUnRead(ctx
context.Context, userID string, b
func (ns *NotificationService) ClearIDUnRead(ctx context.Context, userID
string, id string) error {
notificationInfo, exist, err := ns.notificationRepo.GetById(ctx, id)
if err != nil {
- log.Error("notificationRepo.GetById error", err.Error())
+ log.Errorf("get notification failed: %v", err)
return nil
}
- if !exist {
+ if !exist || notificationInfo.UserID != userID {
return nil
}
- if notificationInfo.UserID == userID && notificationInfo.IsRead ==
schema.NotificationNotRead {
+ if notificationInfo.IsRead == schema.NotificationNotRead {
err := ns.notificationRepo.ClearIDUnRead(ctx, userID, id)
if err != nil {
return err
}
}
+ err = ns.notificationCommon.RemoveBadgeAwardAlertCache(ctx, userID, id)
+ if err != nil {
+ log.Errorf("remove badge award alert cache failed: %v", err)
+ }
return nil
}
@@ -224,6 +250,14 @@ func (ns *NotificationService) formatNotificationPage(ctx
context.Context, notif
item.NotificationAction ==
constant.NotificationDownVotedTheAnswer {
item.UserInfo = nil
}
+ // If notification is badge, the user info is not needed and
the title need to be translated.
+ if item.ObjectInfo.ObjectType == constant.BadgeAwardObjectType {
+ badgeName := translator.Tr(lang, item.ObjectInfo.Title)
+ item.ObjectInfo.Title = translator.TrWithData(lang,
constant.NotificationEarnedBadge, struct {
+ BadgeName string
+ }{BadgeName: badgeName})
+ item.UserInfo = nil
+ }
item.ID = notificationInfo.ID
item.NotificationAction = translator.Tr(lang,
item.NotificationAction)
diff --git a/internal/service/notification_common/notification.go
b/internal/service/notification_common/notification.go
index 319403b2..a3129b3a 100644
--- a/internal/service/notification_common/notification.go
+++ b/internal/service/notification_common/notification.go
@@ -103,7 +103,7 @@ func NewNotificationCommon(
// ObjectInfo.Title
// ObjectInfo.ObjectID
// ObjectInfo.ObjectType
-func (ns *NotificationCommon) AddNotification(ctx context.Context, msg
*schema.NotificationMsg) error {
+func (ns *NotificationCommon) AddNotification(ctx context.Context, msg
*schema.NotificationMsg) (err error) {
if msg.Type == schema.NotificationTypeAchievement &&
plugin.RankAgentEnabled() {
return nil
}
@@ -119,17 +119,25 @@ func (ns *NotificationCommon) AddNotification(ctx
context.Context, msg *schema.N
Type: msg.Type,
}
var questionID string // just for notify all followers
- objInfo, err := ns.objectInfoService.GetInfo(ctx,
req.ObjectInfo.ObjectID)
- if err != nil {
- log.Error(err)
- } else {
- req.ObjectInfo.Title = objInfo.Title
- questionID = objInfo.QuestionID
+ var objInfo *schema.SimpleObjectInfo
+ if msg.ObjectType == constant.BadgeAwardObjectType {
+ req.ObjectInfo.Title = msg.Title
objectMap := make(map[string]string)
- objectMap["question"] = uid.DeShortID(objInfo.QuestionID)
- objectMap["answer"] = uid.DeShortID(objInfo.AnswerID)
- objectMap["comment"] = objInfo.CommentID
+ objectMap["badge_id"] = msg.ExtraInfo["badge_id"]
req.ObjectInfo.ObjectMap = objectMap
+ } else {
+ objInfo, err = ns.objectInfoService.GetInfo(ctx,
req.ObjectInfo.ObjectID)
+ if err != nil {
+ log.Error(err)
+ } else {
+ req.ObjectInfo.Title = objInfo.Title
+ questionID = objInfo.QuestionID
+ objectMap := make(map[string]string)
+ objectMap["question"] =
uid.DeShortID(objInfo.QuestionID)
+ objectMap["answer"] = uid.DeShortID(objInfo.AnswerID)
+ objectMap["comment"] = objInfo.CommentID
+ req.ObjectInfo.ObjectMap = objectMap
+ }
}
if msg.Type == schema.NotificationTypeAchievement {
@@ -188,10 +196,13 @@ func (ns *NotificationCommon) AddNotification(ctx
context.Context, msg *schema.N
if err != nil {
return fmt.Errorf("add notification error: %w", err)
}
- err = ns.addRedDot(ctx, info.UserID, info.Type)
+ err = ns.addRedDot(ctx, info.UserID, msg.Type)
if err != nil {
log.Error("addRedDot Error", err.Error())
}
+ if req.ObjectInfo.ObjectType == constant.BadgeAwardObjectType {
+ err = ns.AddBadgeAwardAlertCache(ctx, info.UserID, info.ID,
req.ObjectInfo.ObjectMap["badge_id"])
+ }
go ns.SendNotificationToAllFollower(ctx, msg, questionID)
@@ -201,19 +212,67 @@ func (ns *NotificationCommon) AddNotification(ctx
context.Context, msg *schema.N
return nil
}
-func (ns *NotificationCommon) addRedDot(ctx context.Context, userID string,
botType int) error {
- key := fmt.Sprintf("answer_RedDot_%d_%s", botType, userID)
- err := ns.data.Cache.SetInt64(ctx, key, 1, 30*24*time.Hour)
//Expiration time is one month.
+func (ns *NotificationCommon) addRedDot(ctx context.Context, userID string,
noticeType int) error {
+ var key string
+ if noticeType == schema.NotificationTypeInbox {
+ key = fmt.Sprintf(constant.RedDotCacheKey,
constant.NotificationTypeInbox, userID)
+ } else {
+ key = fmt.Sprintf(constant.RedDotCacheKey,
constant.NotificationTypeAchievement, userID)
+ }
+ err := ns.data.Cache.SetInt64(ctx, key, 1, constant.RedDotCacheTime)
if err != nil {
return
errors.InternalServer(reason.UnknownError).WithError(err).WithStack()
}
return nil
}
+// AddBadgeAwardAlertCache add badge award alert cache
+func (ns *NotificationCommon) AddBadgeAwardAlertCache(ctx context.Context,
userID, notificationID, badgeID string) (err error) {
+ key := fmt.Sprintf(constant.RedDotCacheKey,
constant.NotificationTypeBadgeAchievement, userID)
+ cacheData, exist, err := ns.data.Cache.GetString(ctx, key)
+ if err != nil {
+ return
errors.InternalServer(reason.UnknownError).WithError(err).WithStack()
+ }
+ if !exist {
+ c := schema.NewRedDotBadgeAwardCache()
+ c.AddBadgeAward(&schema.RedDotBadgeAward{
+ NotificationID: notificationID,
+ BadgeID: badgeID,
+ })
+ return ns.data.Cache.SetString(ctx, key, c.ToJSON(),
constant.RedDotCacheTime)
+ }
+ c := schema.NewRedDotBadgeAwardCache()
+ c.FromJSON(cacheData)
+ c.AddBadgeAward(&schema.RedDotBadgeAward{
+ NotificationID: notificationID,
+ BadgeID: badgeID,
+ })
+ return ns.data.Cache.SetString(ctx, key, c.ToJSON(),
constant.RedDotCacheTime)
+}
+
+// RemoveBadgeAwardAlertCache remove badge award alert cache
+func (ns *NotificationCommon) RemoveBadgeAwardAlertCache(ctx context.Context,
userID, notificationID string) (err error) {
+ key := fmt.Sprintf(constant.RedDotCacheKey,
constant.NotificationTypeBadgeAchievement, userID)
+ cacheData, exist, err := ns.data.Cache.GetString(ctx, key)
+ if err != nil {
+ return
errors.InternalServer(reason.UnknownError).WithError(err).WithStack()
+ }
+ if !exist {
+ return nil
+ }
+ c := schema.NewRedDotBadgeAwardCache()
+ c.FromJSON(cacheData)
+ c.RemoveBadgeAward(notificationID)
+ if len(c.BadgeAwardList) == 0 {
+ return ns.data.Cache.Del(ctx, key)
+ }
+ return ns.data.Cache.SetString(ctx, key, c.ToJSON(),
constant.RedDotCacheTime)
+}
+
// SendNotificationToAllFollower send notification to all followers
func (ns *NotificationCommon) SendNotificationToAllFollower(ctx
context.Context, msg *schema.NotificationMsg,
questionID string) {
- if msg.NoNeedPushAllFollow {
+ if msg.NoNeedPushAllFollow || len(questionID) == 0 {
return
}
if msg.NotificationAction != constant.NotificationUpdateQuestion &&
diff --git a/internal/service/object_info/object_info.go
b/internal/service/object_info/object_info.go
index 6c2e89a9..9a85f07e 100644
--- a/internal/service/object_info/object_info.go
+++ b/internal/service/object_info/object_info.go
@@ -21,7 +21,6 @@ package object_info
import (
"context"
-
"github.com/apache/incubator-answer/internal/base/constant"
"github.com/apache/incubator-answer/internal/base/reason"
"github.com/apache/incubator-answer/internal/schema"