321 lines
9.2 KiB
Go
321 lines
9.2 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"
|
|
"strings"
|
|
|
|
"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/siteinfo_common"
|
|
"github.com/apache/answer/pkg/converter"
|
|
"github.com/apache/answer/pkg/uid"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/segmentfault/pacman/errors"
|
|
"github.com/segmentfault/pacman/log"
|
|
)
|
|
|
|
type BadgeRepo interface {
|
|
GetByID(ctx context.Context, id string) (badge *entity.Badge, exists bool, err error)
|
|
GetByIDs(ctx context.Context, ids []string) (badges []*entity.Badge, err error)
|
|
|
|
ListPaged(ctx context.Context, page int, pageSize int) (badges []*entity.Badge, total int64, err error)
|
|
ListActivated(ctx context.Context, page int, pageSize int) (badges []*entity.Badge, total int64, err error)
|
|
ListInactivated(ctx context.Context, page int, pageSize int) (badges []*entity.Badge, total int64, err error)
|
|
|
|
UpdateStatus(ctx context.Context, id string, status int8) (err error)
|
|
UpdateAwardCount(ctx context.Context, badgeID string, awardCount int) (err error)
|
|
}
|
|
|
|
type BadgeService struct {
|
|
badgeRepo BadgeRepo
|
|
badgeGroupRepo BadgeGroupRepo
|
|
badgeAwardRepo BadgeAwardRepo
|
|
badgeEventService *BadgeEventService
|
|
siteInfoCommonService siteinfo_common.SiteInfoCommonService
|
|
}
|
|
|
|
func NewBadgeService(
|
|
badgeRepo BadgeRepo,
|
|
badgeGroupRepo BadgeGroupRepo,
|
|
badgeAwardRepo BadgeAwardRepo,
|
|
badgeEventService *BadgeEventService,
|
|
siteInfoCommonService siteinfo_common.SiteInfoCommonService,
|
|
) *BadgeService {
|
|
return &BadgeService{
|
|
badgeRepo: badgeRepo,
|
|
badgeGroupRepo: badgeGroupRepo,
|
|
badgeAwardRepo: badgeAwardRepo,
|
|
badgeEventService: badgeEventService,
|
|
siteInfoCommonService: siteInfoCommonService,
|
|
}
|
|
}
|
|
|
|
// 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
|
|
badges []*entity.Badge
|
|
earnedCounts []*entity.BadgeEarnedCount
|
|
|
|
groupMap = make(map[int64]string, 0)
|
|
badgesMap = make(map[int64][]*schema.BadgeListInfo, 0)
|
|
)
|
|
resp = make([]*schema.GetBadgeListResp, 0)
|
|
|
|
groups, err = b.badgeGroupRepo.ListGroups(ctx)
|
|
if err != nil {
|
|
return
|
|
}
|
|
badges, _, err = b.badgeRepo.ListActivated(ctx, 0, 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if len(userID) > 0 {
|
|
earnedCounts, err = b.badgeAwardRepo.SumUserEarnedGroupByBadgeID(ctx, userID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, group := range groups {
|
|
groupMap[converter.StringToInt64(group.ID)] = translator.Tr(handler.GetLangByCtx(ctx), group.Name)
|
|
}
|
|
|
|
for _, badge := range badges {
|
|
// check is earned
|
|
var earned int64 = 0
|
|
if len(earnedCounts) > 0 {
|
|
for _, earnedCount := range earnedCounts {
|
|
if badge.ID == earnedCount.BadgeID && earnedCount.EarnedCount > 0 {
|
|
earned = earnedCount.EarnedCount
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
badgesMap[badge.BadgeGroupID] = append(badgesMap[badge.BadgeGroupID], &schema.BadgeListInfo{
|
|
ID: uid.EnShortID(badge.ID),
|
|
Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name),
|
|
Icon: badge.Icon,
|
|
AwardCount: badge.AwardCount,
|
|
EarnedCount: earned,
|
|
Level: badge.Level,
|
|
})
|
|
}
|
|
|
|
for _, group := range groups {
|
|
resp = append(resp, &schema.GetBadgeListResp{
|
|
GroupName: translator.Tr(handler.GetLangByCtx(ctx), group.Name),
|
|
Badges: badgesMap[converter.StringToInt64(group.ID)],
|
|
})
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// ListPaged list all badges by page
|
|
func (b *BadgeService) ListPaged(ctx context.Context, req *schema.GetBadgeListPagedReq) (resp []*schema.GetBadgeListPagedResp, total int64, err error) {
|
|
var (
|
|
groups []*entity.BadgeGroup
|
|
badges []*entity.Badge
|
|
badge *entity.Badge
|
|
exists bool
|
|
groupMap = make(map[int64]string, 0)
|
|
)
|
|
|
|
total = 0
|
|
|
|
if len(req.Query) > 0 {
|
|
isID := strings.Index(req.Query, "badge:")
|
|
if isID != 0 {
|
|
badges, err = b.searchByName(ctx, req.Query)
|
|
if err != nil {
|
|
return
|
|
}
|
|
// paged result
|
|
count := len(badges)
|
|
total = int64(count)
|
|
start := (req.Page - 1) * req.PageSize
|
|
end := req.Page * req.PageSize
|
|
if start >= count {
|
|
start = count
|
|
end = count
|
|
}
|
|
if end > count {
|
|
end = count
|
|
}
|
|
badges = badges[start:end]
|
|
} else {
|
|
req.Query = strings.TrimSpace(strings.TrimLeft(req.Query, "badge:"))
|
|
id := uid.DeShortID(req.Query)
|
|
if len(id) == 0 {
|
|
return
|
|
}
|
|
badge, exists, err = b.badgeRepo.GetByID(ctx, id)
|
|
if err != nil || !exists {
|
|
return
|
|
}
|
|
badges = append(badges, badge)
|
|
}
|
|
} else {
|
|
switch req.Status {
|
|
case schema.BadgeStatusActive:
|
|
badges, total, err = b.badgeRepo.ListActivated(ctx, req.Page, req.PageSize)
|
|
case schema.BadgeStatusInactive:
|
|
badges, total, err = b.badgeRepo.ListInactivated(ctx, req.Page, req.PageSize)
|
|
default:
|
|
badges, total, err = b.badgeRepo.ListPaged(ctx, req.Page, req.PageSize)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
// find all group and build group map
|
|
groups, err = b.badgeGroupRepo.ListGroups(ctx)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, group := range groups {
|
|
groupMap[converter.StringToInt64(group.ID)] = translator.Tr(handler.GetLangByCtx(ctx), group.Name)
|
|
}
|
|
|
|
resp = make([]*schema.GetBadgeListPagedResp, len(badges))
|
|
|
|
general, siteErr := b.siteInfoCommonService.GetSiteGeneral(ctx)
|
|
baseURL := ""
|
|
if siteErr == nil {
|
|
baseURL = general.SiteUrl
|
|
}
|
|
|
|
for i, badge := range badges {
|
|
resp[i] = &schema.GetBadgeListPagedResp{
|
|
ID: uid.EnShortID(badge.ID),
|
|
Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name),
|
|
Description: translator.TrWithData(handler.GetLangByCtx(ctx), badge.Description, &schema.BadgeTplData{ProfileURL: baseURL + "/users/settings/profile"}),
|
|
Icon: badge.Icon,
|
|
AwardCount: badge.AwardCount,
|
|
Level: badge.Level,
|
|
GroupName: groupMap[badge.BadgeGroupID],
|
|
Status: schema.BadgeStatusMap[badge.Status],
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// searchByName
|
|
func (b *BadgeService) searchByName(ctx context.Context, name string) (result []*entity.Badge, err error) {
|
|
var badges []*entity.Badge
|
|
name = strings.ToLower(name)
|
|
result = make([]*entity.Badge, 0)
|
|
|
|
badges, _, err = b.badgeRepo.ListPaged(ctx, 0, 0)
|
|
for _, badge := range badges {
|
|
tn := strings.ToLower(translator.Tr(handler.GetLangByCtx(ctx), badge.Name))
|
|
if strings.Contains(tn, name) {
|
|
result = append(result, badge)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetBadgeInfo get badge info
|
|
func (b *BadgeService) GetBadgeInfo(ctx *gin.Context, id string, userID string) (info *schema.GetBadgeInfoResp, err error) {
|
|
badge, exists, err := b.badgeRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !exists || badge.Status == entity.BadgeStatusInactive {
|
|
return nil, errors.BadRequest(reason.BadgeObjectNotFound)
|
|
}
|
|
|
|
var earnedTotal int64
|
|
if len(userID) > 0 {
|
|
earnedTotal = b.badgeAwardRepo.CountByUserIdAndBadgeId(ctx, userID, badge.ID)
|
|
}
|
|
|
|
baseURL := ""
|
|
general, siteErr := b.siteInfoCommonService.GetSiteGeneral(ctx)
|
|
if siteErr == nil {
|
|
baseURL = general.SiteUrl
|
|
}
|
|
|
|
info = &schema.GetBadgeInfoResp{
|
|
ID: uid.EnShortID(badge.ID),
|
|
Name: translator.Tr(handler.GetLangByCtx(ctx), badge.Name),
|
|
Description: translator.TrWithData(handler.GetLangByCtx(ctx), badge.Description, &schema.BadgeTplData{ProfileURL: baseURL + "/users/settings/profile"}),
|
|
Icon: badge.Icon,
|
|
AwardCount: badge.AwardCount,
|
|
EarnedCount: earnedTotal,
|
|
IsSingle: badge.Single == entity.BadgeSingleAward,
|
|
Level: badge.Level,
|
|
}
|
|
return
|
|
}
|
|
|
|
// UpdateStatus update badge status
|
|
func (b *BadgeService) UpdateStatus(ctx *gin.Context, req *schema.UpdateBadgeStatusReq) (err error) {
|
|
req.ID = uid.DeShortID(req.ID)
|
|
|
|
badge, exists, err := b.badgeRepo.GetByID(ctx, req.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
return errors.BadRequest(reason.BadgeObjectNotFound)
|
|
}
|
|
|
|
// check duplicate action
|
|
status, ok := schema.BadgeStatusEMap[req.Status]
|
|
if !ok {
|
|
err = errors.BadRequest(reason.StatusInvalid)
|
|
return
|
|
}
|
|
if badge.Status == status {
|
|
return
|
|
}
|
|
|
|
err = b.badgeRepo.UpdateStatus(ctx, req.ID, status)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if status == entity.BadgeStatusActive {
|
|
count, err := b.badgeAwardRepo.CountByBadgeID(ctx, badge.ID)
|
|
if err != nil {
|
|
log.Errorf("count badge award failed: %v", err)
|
|
return nil
|
|
}
|
|
err = b.badgeRepo.UpdateAwardCount(ctx, badge.ID, int(count))
|
|
if err != nil {
|
|
log.Errorf("update badge award count failed: %v", err)
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
}
|