303 lines
9.1 KiB
Go
303 lines
9.1 KiB
Go
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
package badge
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/apache/answer/internal/base/constant"
|
|
"github.com/apache/answer/internal/base/handler"
|
|
"github.com/apache/answer/internal/base/reason"
|
|
"github.com/apache/answer/internal/base/translator"
|
|
"github.com/apache/answer/internal/entity"
|
|
"github.com/apache/answer/internal/schema"
|
|
"github.com/apache/answer/internal/service/noticequeue"
|
|
"github.com/apache/answer/internal/service/object_info"
|
|
usercommon "github.com/apache/answer/internal/service/user_common"
|
|
"github.com/apache/answer/pkg/uid"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/jinzhu/copier"
|
|
"github.com/segmentfault/pacman/errors"
|
|
"github.com/segmentfault/pacman/log"
|
|
)
|
|
|
|
type BadgeAwardRepo interface {
|
|
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)
|
|
|
|
CountByUserIdAndBadgeId(ctx context.Context, userID string, badgeID string) (awardCount int64)
|
|
CountByBadgeID(ctx context.Context, badgeID string) (awardCount int64, err error)
|
|
|
|
SumUserEarnedGroupByBadgeID(ctx context.Context, userID string) (earnedCounts []*entity.BadgeEarnedCount, err error)
|
|
|
|
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)
|
|
ListNewestEarned(ctx context.Context, userID string, limit int) (badgeAwards []*entity.BadgeAwardRecent, err error)
|
|
|
|
GetByUserIdAndBadgeId(ctx context.Context, userID string, badgeID 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)
|
|
|
|
DeleteUserBadgeAward(ctx context.Context, userID string) (err error)
|
|
}
|
|
|
|
type BadgeAwardService struct {
|
|
badgeAwardRepo BadgeAwardRepo
|
|
badgeRepo BadgeRepo
|
|
userCommon *usercommon.UserCommon
|
|
objectInfoService *object_info.ObjService
|
|
notificationQueueService noticequeue.Service
|
|
}
|
|
|
|
func NewBadgeAwardService(
|
|
badgeAwardRepo BadgeAwardRepo,
|
|
badgeRepo BadgeRepo,
|
|
userCommon *usercommon.UserCommon,
|
|
objectInfoService *object_info.ObjService,
|
|
notificationQueueService noticequeue.Service,
|
|
) *BadgeAwardService {
|
|
return &BadgeAwardService{
|
|
badgeAwardRepo: badgeAwardRepo,
|
|
badgeRepo: badgeRepo,
|
|
userCommon: userCommon,
|
|
objectInfoService: objectInfoService,
|
|
notificationQueueService: notificationQueueService,
|
|
}
|
|
}
|
|
|
|
// GetBadgeAwardList get badge award list
|
|
func (bs *BadgeAwardService) GetBadgeAwardList(
|
|
ctx context.Context,
|
|
req *schema.GetBadgeAwardWithPageReq,
|
|
) (resp []*schema.GetBadgeAwardWithPageResp, total int64, err error) {
|
|
var (
|
|
badgeAwardList []*entity.BadgeAward
|
|
)
|
|
|
|
req.UserID, err = bs.validateUserByUsername(ctx, req.Username)
|
|
if err != nil {
|
|
badgeAwardList, total, err = bs.badgeAwardRepo.ListPagedByBadgeId(ctx, req.BadgeID, req.Page, req.PageSize)
|
|
} else {
|
|
badgeAwardList, total, err = bs.badgeAwardRepo.ListPagedByBadgeIdAndUserId(ctx, req.BadgeID, req.UserID, req.Page, req.PageSize)
|
|
}
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
resp = make([]*schema.GetBadgeAwardWithPageResp, len(badgeAwardList))
|
|
|
|
for i, badgeAward := range badgeAwardList {
|
|
var (
|
|
objectID, questionID, answerID, commentID, objectType, urlTitle string
|
|
)
|
|
|
|
// if exist object info
|
|
objInfo, e := bs.objectInfoService.GetInfo(ctx, badgeAward.AwardKey)
|
|
if e == nil && !objInfo.IsDeleted() {
|
|
objectID = objInfo.ObjectID
|
|
questionID = objInfo.QuestionID
|
|
answerID = objInfo.AnswerID
|
|
commentID = objInfo.CommentID
|
|
objectType = objInfo.ObjectType
|
|
urlTitle = objInfo.Title
|
|
}
|
|
|
|
row := &schema.GetBadgeAwardWithPageResp{
|
|
CreatedAt: badgeAward.CreatedAt.Unix(),
|
|
ObjectID: objectID,
|
|
QuestionID: questionID,
|
|
AnswerID: answerID,
|
|
CommentID: commentID,
|
|
ObjectType: objectType,
|
|
UrlTitle: urlTitle,
|
|
AuthorUserInfo: schema.UserBasicInfo{},
|
|
}
|
|
|
|
// get user info
|
|
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)
|
|
}
|
|
if exists {
|
|
_ = copier.Copy(&row.AuthorUserInfo, userInfo)
|
|
}
|
|
|
|
resp[i] = row
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Award award badge
|
|
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
|
|
}
|
|
|
|
if !exists || badgeData.Status == entity.BadgeStatusInactive {
|
|
return errors.BadRequest(reason.BadgeObjectNotFound)
|
|
}
|
|
|
|
alreadyAwarded, err := bs.badgeAwardRepo.CheckIsAward(ctx, badgeID, userID, awardKey, badgeData.Single)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if alreadyAwarded {
|
|
return nil
|
|
}
|
|
|
|
badgeAward := &entity.BadgeAward{
|
|
UserID: userID,
|
|
BadgeID: badgeID,
|
|
AwardKey: awardKey,
|
|
BadgeGroupID: badgeData.BadgeGroupID,
|
|
IsBadgeDeleted: entity.IsBadgeNotDeleted,
|
|
}
|
|
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 (bs *BadgeAwardService) GetUserBadgeAwardList(
|
|
ctx *gin.Context,
|
|
req *schema.GetUserBadgeAwardListReq,
|
|
) (
|
|
resp []*schema.GetUserBadgeAwardListResp,
|
|
total int64,
|
|
err error,
|
|
) {
|
|
var (
|
|
earnedCounts []*entity.BadgeEarnedCount
|
|
)
|
|
|
|
req.UserID, err = bs.validateUserByUsername(ctx, req.Username)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
earnedCounts, err = bs.badgeAwardRepo.SumUserEarnedGroupByBadgeID(ctx, req.UserID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
total = int64(len(earnedCounts))
|
|
resp = make([]*schema.GetUserBadgeAwardListResp, total)
|
|
|
|
for i, earnedCount := range earnedCounts {
|
|
badge, exists, e := bs.badgeRepo.GetByID(ctx, earnedCount.BadgeID)
|
|
if e != nil {
|
|
err = e
|
|
return
|
|
}
|
|
if !exists {
|
|
continue
|
|
}
|
|
resp[i] = &schema.GetUserBadgeAwardListResp{
|
|
ID: uid.EnShortID(badge.ID),
|
|
Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name),
|
|
Icon: badge.Icon,
|
|
EarnedCount: earnedCount.EarnedCount,
|
|
Level: badge.Level,
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// GetUserRecentBadgeAwardList get user badge award list
|
|
func (bs *BadgeAwardService) GetUserRecentBadgeAwardList(ctx *gin.Context, req *schema.GetUserBadgeAwardListReq) (
|
|
resp []*schema.GetUserBadgeAwardListResp, total int64, err error) {
|
|
var (
|
|
earnedCounts []*entity.BadgeAwardRecent
|
|
)
|
|
|
|
req.UserID, err = bs.validateUserByUsername(ctx, req.Username)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
earnedCounts, err = bs.badgeAwardRepo.ListNewestEarned(ctx, req.UserID, req.Limit)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
total = int64(len(earnedCounts))
|
|
resp = make([]*schema.GetUserBadgeAwardListResp, total)
|
|
|
|
for i, earnedCount := range earnedCounts {
|
|
badge, exists, e := bs.badgeRepo.GetByID(ctx, earnedCount.BadgeID)
|
|
if e != nil {
|
|
err = e
|
|
return
|
|
}
|
|
if !exists {
|
|
continue
|
|
}
|
|
resp[i] = &schema.GetUserBadgeAwardListResp{
|
|
ID: uid.EnShortID(badge.ID),
|
|
Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name),
|
|
Icon: badge.Icon,
|
|
EarnedCount: earnedCount.EarnedCount,
|
|
Level: badge.Level,
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
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 = bs.userCommon.GetUserBasicInfoByUserName(ctx, userName)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !exist {
|
|
err = errors.BadRequest(reason.UserNotFound)
|
|
return
|
|
}
|
|
userID = userInfo.ID
|
|
}
|
|
if len(userID) == 0 {
|
|
err = errors.BadRequest(reason.UserNotFound)
|
|
return
|
|
}
|
|
return
|
|
}
|