185 lines
4.5 KiB
Go
185 lines
4.5 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"apps/backend/internal/module/usage/domain"
|
|
)
|
|
|
|
type MemoryStore struct {
|
|
mu sync.Mutex
|
|
events []*domain.Event
|
|
counters map[string]*domain.MonthlyCounter
|
|
prefs map[int64]*domain.MemberPrefs
|
|
purchases []*domain.Purchase
|
|
}
|
|
|
|
func NewMemory() *MemoryStore {
|
|
return &MemoryStore{
|
|
counters: map[string]*domain.MonthlyCounter{},
|
|
prefs: map[int64]*domain.MemberPrefs{},
|
|
}
|
|
}
|
|
|
|
func memoryCounterID(uid int64, monthKey string) string {
|
|
return fmt.Sprintf("%d:%s", uid, monthKey)
|
|
}
|
|
|
|
func (s *MemoryStore) ReservePlatform(_ context.Context, uid int64, monthKey, meter string, cost, totalLimit, meterLimit int) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
id := memoryCounterID(uid, monthKey)
|
|
counter := s.counters[id]
|
|
if counter == nil {
|
|
counter = &domain.MonthlyCounter{ID: id, UID: uid, MonthKey: monthKey, ByMeter: map[string]int{}}
|
|
for _, event := range s.events {
|
|
if event.UID == uid && event.MonthKey == monthKey && event.KeyMode == domain.KeyModePlatform {
|
|
credits := event.Credits
|
|
if minimum := domain.MeterCost(event.Meter); credits < minimum {
|
|
credits = minimum
|
|
}
|
|
counter.TotalCredits += credits
|
|
counter.ByMeter[event.Meter] += credits
|
|
}
|
|
}
|
|
s.counters[id] = counter
|
|
}
|
|
if totalLimit >= 0 && counter.TotalCredits+cost > totalLimit {
|
|
return domain.ErrQuotaExceeded
|
|
}
|
|
if meterLimit >= 0 && counter.ByMeter[meter]+cost > meterLimit {
|
|
return domain.ErrMeterCapExceeded
|
|
}
|
|
counter.TotalCredits += cost
|
|
counter.ByMeter[meter] += cost
|
|
counter.UpdatedAt = domain.NowNano()
|
|
return nil
|
|
}
|
|
|
|
func (s *MemoryStore) GetMonthlyCounter(_ context.Context, uid int64, monthKey string) (*domain.MonthlyCounter, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
counter := s.counters[memoryCounterID(uid, monthKey)]
|
|
if counter == nil {
|
|
return nil, domain.ErrNotFound
|
|
}
|
|
copyCounter := *counter
|
|
copyCounter.ByMeter = make(map[string]int, len(counter.ByMeter))
|
|
for meter, credits := range counter.ByMeter {
|
|
copyCounter.ByMeter[meter] = credits
|
|
}
|
|
return ©Counter, nil
|
|
}
|
|
|
|
func (s *MemoryStore) InsertEvent(_ context.Context, e *domain.Event) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cp := *e
|
|
s.events = append(s.events, &cp)
|
|
return nil
|
|
}
|
|
|
|
func (s *MemoryStore) ListEvents(_ context.Context, uid int64, monthKey, keyMode string, limit int) ([]*domain.Event, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
var out []*domain.Event
|
|
for i := len(s.events) - 1; i >= 0; i-- {
|
|
e := s.events[i]
|
|
if e.UID != uid {
|
|
continue
|
|
}
|
|
if monthKey != "" && e.MonthKey != monthKey {
|
|
continue
|
|
}
|
|
if keyMode != "" && keyMode != "all" && e.KeyMode != keyMode {
|
|
continue
|
|
}
|
|
cp := *e
|
|
out = append(out, &cp)
|
|
if limit > 0 && len(out) >= limit {
|
|
break
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *MemoryStore) ListEventsAll(_ context.Context, monthKey string, limit int) ([]*domain.Event, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
var out []*domain.Event
|
|
for i := len(s.events) - 1; i >= 0; i-- {
|
|
e := s.events[i]
|
|
if monthKey != "" && e.MonthKey != monthKey {
|
|
continue
|
|
}
|
|
cp := *e
|
|
out = append(out, &cp)
|
|
if limit > 0 && len(out) >= limit {
|
|
break
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *MemoryStore) ListEventsRange(_ context.Context, fromNano, toExclusiveNano int64) ([]*domain.Event, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
out := make([]*domain.Event, 0)
|
|
for _, event := range s.events {
|
|
if event.CreatedAt < fromNano || event.CreatedAt >= toExclusiveNano {
|
|
continue
|
|
}
|
|
cp := *event
|
|
out = append(out, &cp)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *MemoryStore) GetPrefs(_ context.Context, uid int64) (*domain.MemberPrefs, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if p, ok := s.prefs[uid]; ok {
|
|
cp := *p
|
|
return &cp, nil
|
|
}
|
|
return &domain.MemberPrefs{UID: uid, PlanID: domain.PlanFree}, nil
|
|
}
|
|
|
|
func (s *MemoryStore) SavePrefs(_ context.Context, p *domain.MemberPrefs) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cp := *p
|
|
s.prefs[p.UID] = &cp
|
|
return nil
|
|
}
|
|
|
|
func (s *MemoryStore) InsertPurchase(_ context.Context, p *domain.Purchase) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cp := *p
|
|
s.purchases = append(s.purchases, &cp)
|
|
return nil
|
|
}
|
|
|
|
func (s *MemoryStore) ListPurchases(_ context.Context, uid int64, limit int) ([]*domain.Purchase, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
var out []*domain.Purchase
|
|
for i := len(s.purchases) - 1; i >= 0; i-- {
|
|
p := s.purchases[i]
|
|
if p.UID != uid {
|
|
continue
|
|
}
|
|
cp := *p
|
|
out = append(out, &cp)
|
|
if limit > 0 && len(out) >= limit {
|
|
break
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
var _ domain.Repository = (*MemoryStore)(nil)
|