Files
kra-new/internal/biz/gift/gift.go
T

159 lines
5.0 KiB
Go

package gift
import (
"context"
kratoserrors "github.com/go-kratos/kratos/v3/errors"
"sort"
"strings"
"time"
)
var (
ErrNotFound = kratoserrors.NotFound("GIFT_NOT_FOUND", "gift not found")
ErrNameExists = kratoserrors.BadRequest("GIFT_NAME_EXISTS", "gift name already exists")
ErrInvalid = kratoserrors.BadRequest("GIFT_INVALID", "invalid gift data")
ErrLastEnabled = kratoserrors.BadRequest("GIFT_LAST_ENABLED", "at least one enabled gift is required")
ErrInsufficientBalance = kratoserrors.BadRequest("GIFT_INSUFFICIENT_BALANCE", "insufficient diamond balance")
ErrSendNotAllowed = kratoserrors.BadRequest("GIFT_SEND_NOT_ALLOWED", "gift cannot be sent")
)
type Gift struct {
ID uint
Name, Icon, Description string
Price int64
Tiers []int
Sort int
Enabled bool
SendCount int64
CreatedAt, UpdatedAt time.Time
}
type Send struct {
ID uint
SerialNo string
UserID uint
Username, Nickname string
RoomID *uint
RoomName string
RoomDeleted bool
AnchorUserID uint
AnchorName string
GiftID *uint
GiftName, GiftIcon string
GiftPrice int64
Quantity int
DiamondTotal, RateCents, EquivalentCents int64
CreatedAt time.Time
}
type Filter struct {
Name string
MinPrice, MaxPrice int64
Enabled *bool
}
type SendFilter struct {
RoomName, AnchorName, UserKeyword string
GiftID uint
CreatedAt []time.Time
}
type ListOptions struct {
Offset, Limit int
OrderBy string
}
type Summary struct{ Count, Diamonds, AmountCents int64 }
type Repo interface {
List(context.Context, Filter, ListOptions) ([]*Gift, int64, error)
Get(context.Context, uint) (*Gift, error)
Save(context.Context, *Gift) (*Gift, error)
Delete(context.Context, uint) error
SetEnabled(context.Context, uint, bool) error
SaveSort(context.Context, map[uint]int) error
ListSends(context.Context, SendFilter, ListOptions) ([]*Send, int64, *Summary, error)
Send(context.Context, uint, uint, uint, int) (*Send, error)
}
type Notifier interface {
GiftSent(context.Context, *Send)
}
type Usecase struct {
repo Repo
notifier Notifier
}
func NewUsecase(repo Repo, notifier Notifier) *Usecase {
return &Usecase{repo: repo, notifier: notifier}
}
func (uc *Usecase) List(ctx context.Context, f Filter, o ListOptions) ([]*Gift, int64, error) {
f.Name = strings.TrimSpace(f.Name)
return uc.repo.List(ctx, f, normalize(o))
}
func (uc *Usecase) Get(ctx context.Context, id uint) (*Gift, error) {
if id == 0 {
return nil, ErrNotFound
}
return uc.repo.Get(ctx, id)
}
func (uc *Usecase) Save(ctx context.Context, item *Gift) (*Gift, error) {
if err := validate(item); err != nil {
return nil, err
}
return uc.repo.Save(ctx, item)
}
func (uc *Usecase) Delete(ctx context.Context, id uint) error { return uc.repo.Delete(ctx, id) }
func (uc *Usecase) SetEnabled(ctx context.Context, id uint, enabled bool) error {
return uc.repo.SetEnabled(ctx, id, enabled)
}
func (uc *Usecase) SaveSort(ctx context.Context, values map[uint]int) error {
for id, v := range values {
if id == 0 || v < 0 || v > 9999 {
return ErrInvalid
}
}
return uc.repo.SaveSort(ctx, values)
}
func (uc *Usecase) ListSends(ctx context.Context, f SendFilter, o ListOptions) ([]*Send, int64, *Summary, error) {
return uc.repo.ListSends(ctx, f, normalize(o))
}
func (uc *Usecase) Send(ctx context.Context, userID, roomID, giftID uint, quantity int) (*Send, error) {
if userID == 0 || roomID == 0 || giftID == 0 || quantity < 1 || quantity > 9999 {
return nil, ErrSendNotAllowed
}
result, err := uc.repo.Send(ctx, userID, roomID, giftID, quantity)
if err != nil {
return nil, err
}
if uc.notifier != nil {
uc.notifier.GiftSent(ctx, result)
}
return result, nil
}
func normalize(o ListOptions) ListOptions {
if o.Offset < 0 {
o.Offset = 0
}
if o.Limit < 1 || o.Limit > 100 {
o.Limit = 10
}
return o
}
func validate(item *Gift) error {
if item == nil {
return ErrInvalid
}
item.Name = strings.TrimSpace(item.Name)
item.Icon = strings.TrimSpace(item.Icon)
item.Description = strings.TrimSpace(item.Description)
if len([]rune(item.Name)) < 1 || len([]rune(item.Name)) > 12 || item.Icon == "" || item.Price < 1 || item.Price > 99999 || len([]rune(item.Description)) > 50 || item.Sort < 0 || item.Sort > 9999 || len(item.Tiers) < 1 || len(item.Tiers) > 6 {
return ErrInvalid
}
seen := map[int]struct{}{}
for _, v := range item.Tiers {
if v < 1 || v > 9999 {
return ErrInvalid
}
if _, ok := seen[v]; ok {
return ErrInvalid
}
seen[v] = struct{}{}
}
sort.Ints(item.Tiers)
return nil
}