124 lines
3.8 KiB
Go
124 lines
3.8 KiB
Go
// Package stargifts 实现 Star 礼物应用服务:礼物目录(从 seed 合成、懒加载缓存)+ peer 收到的
|
||
// 礼物实例 CRUD。扣费/退款/服务消息投递由 rpc 层编排(复用 Stars 账本 + SendPrivateText),
|
||
// 本层只管目录与持久化。
|
||
package stargifts
|
||
|
||
import (
|
||
"context"
|
||
"sync"
|
||
|
||
"telesrv/internal/domain"
|
||
"telesrv/internal/store"
|
||
)
|
||
|
||
// CatalogProvider 合成礼物目录(app/files 实现)。
|
||
type CatalogProvider interface {
|
||
BuildStarGiftCatalog(ctx context.Context) ([]domain.StarGift, error)
|
||
}
|
||
|
||
// Service 是 Star 礼物应用服务。
|
||
type Service struct {
|
||
store store.StarGiftStore
|
||
catalog CatalogProvider
|
||
|
||
mu sync.Mutex
|
||
built bool
|
||
gifts []domain.StarGift
|
||
byID map[int64]domain.StarGift
|
||
hash int
|
||
}
|
||
|
||
// NewService 创建 Star 礼物服务。
|
||
func NewService(st store.StarGiftStore, catalog CatalogProvider) *Service {
|
||
return &Service{store: st, catalog: catalog}
|
||
}
|
||
|
||
// ensureCatalog 懒加载并缓存目录(静态数据,构建一次)。
|
||
func (s *Service) ensureCatalog(ctx context.Context) error {
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
if s.built {
|
||
return nil
|
||
}
|
||
gifts, err := s.catalog.BuildStarGiftCatalog(ctx)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
s.gifts = gifts
|
||
s.byID = make(map[int64]domain.StarGift, len(gifts))
|
||
for _, g := range gifts {
|
||
s.byID[g.ID] = g
|
||
}
|
||
s.hash = domain.StarGiftCatalogHash(gifts)
|
||
s.built = true
|
||
return nil
|
||
}
|
||
|
||
// Catalog 返回礼物目录。
|
||
func (s *Service) Catalog(ctx context.Context) ([]domain.StarGift, error) {
|
||
if err := s.ensureCatalog(ctx); err != nil {
|
||
return nil, err
|
||
}
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
out := make([]domain.StarGift, len(s.gifts))
|
||
copy(out, s.gifts)
|
||
return out, nil
|
||
}
|
||
|
||
// CatalogHash 返回目录 hash(getStarGifts NotModified 判定)。
|
||
func (s *Service) CatalogHash(ctx context.Context) (int, error) {
|
||
if err := s.ensureCatalog(ctx); err != nil {
|
||
return 0, err
|
||
}
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
return s.hash, nil
|
||
}
|
||
|
||
// GiftByID 返回目录中指定礼物,不存在返回 ok=false。
|
||
func (s *Service) GiftByID(ctx context.Context, id int64) (domain.StarGift, bool, error) {
|
||
if err := s.ensureCatalog(ctx); err != nil {
|
||
return domain.StarGift{}, false, err
|
||
}
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
g, ok := s.byID[id]
|
||
return g, ok, nil
|
||
}
|
||
|
||
// RecordSavedGift 持久化一条收到的礼物实例,返回行 id。
|
||
func (s *Service) RecordSavedGift(ctx context.Context, gift domain.SavedStarGift) (int64, error) {
|
||
return s.store.Create(ctx, gift)
|
||
}
|
||
|
||
// ListSaved 分页返回某 owner 收到的礼物。
|
||
func (s *Service) ListSaved(ctx context.Context, owner domain.Peer, excludeUnsaved bool, offset string, limit int) (domain.SavedStarGiftPage, error) {
|
||
if len(offset) > domain.MaxStarGiftsOffsetBytes {
|
||
offset = ""
|
||
}
|
||
if limit <= 0 || limit > domain.MaxSavedStarGiftsLimit {
|
||
limit = domain.MaxSavedStarGiftsLimit
|
||
}
|
||
return s.store.ListByOwner(ctx, owner, excludeUnsaved, offset, limit)
|
||
}
|
||
|
||
// GetSaved 按协议引用取礼物实例。
|
||
func (s *Service) GetSaved(ctx context.Context, ref domain.SavedStarGiftRef) (domain.SavedStarGift, bool, error) {
|
||
return s.store.GetByRef(ctx, ref)
|
||
}
|
||
|
||
// CountSaved 返回某 owner 展示在资料的礼物数(full.stargifts_count)。
|
||
func (s *Service) CountSaved(ctx context.Context, owner domain.Peer) (int, error) {
|
||
return s.store.CountByOwner(ctx, owner)
|
||
}
|
||
|
||
// ToggleSaved 切换礼物在资料的展示(saveStarGift)。
|
||
func (s *Service) ToggleSaved(ctx context.Context, ref domain.SavedStarGiftRef, unsaved bool) (bool, error) {
|
||
return s.store.SetUnsaved(ctx, ref, unsaved)
|
||
}
|
||
|
||
// Convert 把礼物标记为已转换(convertStarGift),返回该行供调用方据 ConvertStars 入账。
|
||
func (s *Service) Convert(ctx context.Context, ref domain.SavedStarGiftRef) (domain.SavedStarGift, error) {
|
||
return s.store.MarkConverted(ctx, ref)
|
||
}
|