thread-master/apps/backend/internal/module/usage/repository/memory.go

185 lines
4.5 KiB
Go
Raw Normal View History

2026-07-13 01:15:30 +00:00
package repository
import (
"context"
2026-07-15 15:23:59 +00:00
"fmt"
2026-07-13 01:15:30 +00:00
"sync"
"apps/backend/internal/module/usage/domain"
)
type MemoryStore struct {
mu sync.Mutex
events []*domain.Event
2026-07-15 15:23:59 +00:00
counters map[string]*domain.MonthlyCounter
2026-07-13 01:15:30 +00:00
prefs map[int64]*domain.MemberPrefs
purchases []*domain.Purchase
}
func NewMemory() *MemoryStore {
return &MemoryStore{
2026-07-15 15:23:59 +00:00
counters: map[string]*domain.MonthlyCounter{},
prefs: map[int64]*domain.MemberPrefs{},
2026-07-13 01:15:30 +00:00
}
}
2026-07-15 15:23:59 +00:00
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 &copyCounter, nil
}
2026-07-13 01:15:30 +00:00
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
}
2026-07-15 15:23:59 +00:00
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
}
2026-07-13 01:15:30 +00:00
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)