管理端按用户查询开启通讯录/相册/媒体拉取;服务端写入路径自动压图;新增 /v1/sign 与 /v1/energy。 Co-authored-by: Cursor <cursoragent@cursor.com>
174 lines
4.0 KiB
Go
174 lines
4.0 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
"spark-server/internal/module/energy/entity"
|
|
"spark-server/internal/pkg/errs"
|
|
)
|
|
|
|
// MemoryRepo is an in-process store for logic tests.
|
|
type MemoryRepo struct {
|
|
mu sync.Mutex
|
|
wallet map[int64]*entity.Wallet
|
|
signs map[int64]map[int]*entity.SignRecord
|
|
logs []*entity.EnergyLog
|
|
nextID int64
|
|
}
|
|
|
|
func NewMemoryRepo() *MemoryRepo {
|
|
return &MemoryRepo{
|
|
wallet: make(map[int64]*entity.Wallet),
|
|
signs: make(map[int64]map[int]*entity.SignRecord),
|
|
nextID: 1,
|
|
}
|
|
}
|
|
|
|
func (m *MemoryRepo) GetWallet(_ context.Context, userID int64) (*entity.Wallet, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
w, ok := m.wallet[userID]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
cp := *w
|
|
return &cp, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) EnsureWallet(_ context.Context, userID int64) (*entity.Wallet, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if w, ok := m.wallet[userID]; ok {
|
|
cp := *w
|
|
return &cp, nil
|
|
}
|
|
w := &entity.Wallet{UserID: userID, Energy: 0, UpdatedMS: time.Now().UnixMilli()}
|
|
m.wallet[userID] = w
|
|
cp := *w
|
|
return &cp, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) AddEnergy(_ context.Context, userID, delta int64, reason string, dayKey int, source string) (*entity.Wallet, *entity.EnergyLog, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
w, ok := m.wallet[userID]
|
|
if !ok {
|
|
w = &entity.Wallet{UserID: userID}
|
|
m.wallet[userID] = w
|
|
}
|
|
w.Energy += delta
|
|
if w.Energy < 0 {
|
|
return nil, nil, errs.ErrInvalidArgument
|
|
}
|
|
w.UpdatedMS = time.Now().UnixMilli()
|
|
id := m.nextID
|
|
m.nextID++
|
|
log := &entity.EnergyLog{
|
|
ID: id, UserID: userID, Delta: delta, BalanceAfter: w.Energy,
|
|
Reason: reason, DayKey: dayKey, Source: source, CreatedMS: w.UpdatedMS,
|
|
}
|
|
m.logs = append(m.logs, log)
|
|
wc, lc := *w, *log
|
|
return &wc, &lc, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListSignsInRange(_ context.Context, userID int64, fromDay, toDay int) ([]*entity.SignRecord, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
byDay := m.signs[userID]
|
|
out := make([]*entity.SignRecord, 0)
|
|
for day, rec := range byDay {
|
|
if day >= fromDay && day <= toDay {
|
|
cp := *rec
|
|
out = append(out, &cp)
|
|
}
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].DayKey < out[j].DayKey })
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) GetSign(_ context.Context, userID int64, dayKey int) (*entity.SignRecord, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
byDay := m.signs[userID]
|
|
if byDay == nil {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
rec, ok := byDay[dayKey]
|
|
if !ok {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
cp := *rec
|
|
return &cp, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) CreateSign(_ context.Context, rec *entity.SignRecord) error {
|
|
if rec == nil || rec.UserID <= 0 || rec.DayKey <= 0 {
|
|
return errs.ErrInvalidArgument
|
|
}
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
byDay := m.signs[rec.UserID]
|
|
if byDay == nil {
|
|
byDay = make(map[int]*entity.SignRecord)
|
|
m.signs[rec.UserID] = byDay
|
|
}
|
|
if _, ok := byDay[rec.DayKey]; ok {
|
|
return errs.ErrDuplicate
|
|
}
|
|
if rec.ID == 0 {
|
|
rec.ID = m.nextID
|
|
m.nextID++
|
|
}
|
|
if rec.CreatedMS == 0 {
|
|
rec.CreatedMS = time.Now().UnixMilli()
|
|
}
|
|
cp := *rec
|
|
byDay[rec.DayKey] = &cp
|
|
return nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListEnergyLogs(_ context.Context, userID, cursor int64, limit int) ([]*entity.EnergyLog, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
limit = entity.ClampLimit(limit)
|
|
out := make([]*entity.EnergyLog, 0)
|
|
for i := len(m.logs) - 1; i >= 0; i-- {
|
|
row := m.logs[i]
|
|
if row.UserID != userID {
|
|
continue
|
|
}
|
|
if cursor > 0 && row.ID >= cursor {
|
|
continue
|
|
}
|
|
cp := *row
|
|
out = append(out, &cp)
|
|
if len(out) >= limit {
|
|
break
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemoryRepo) ListRecentSigns(_ context.Context, userID int64, limit int) ([]*entity.SignRecord, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if limit <= 0 {
|
|
limit = 40
|
|
}
|
|
byDay := m.signs[userID]
|
|
out := make([]*entity.SignRecord, 0, len(byDay))
|
|
for _, rec := range byDay {
|
|
cp := *rec
|
|
out = append(out, &cp)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].DayKey > out[j].DayKey })
|
|
if len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|