chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
143
internal/store/memory/stars.go
Normal file
143
internal/store/memory/stars.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// StarsStore 是 store.StarsStore 的内存实现,复刻 postgres 版的原子语义
|
||||
// (在单个互斥锁下完成读-检查-写,等价于 SELECT ... FOR UPDATE)。
|
||||
type StarsStore struct {
|
||||
mu sync.Mutex
|
||||
states map[int64]*starsState
|
||||
nextID int64
|
||||
}
|
||||
|
||||
type starsState struct {
|
||||
balance int64
|
||||
granted bool
|
||||
txns []domain.StarsTransaction // 追加序,读时倒序
|
||||
}
|
||||
|
||||
// NewStarsStore 创建内存 StarsStore。
|
||||
func NewStarsStore() *StarsStore {
|
||||
return &StarsStore{states: make(map[int64]*starsState)}
|
||||
}
|
||||
|
||||
func (s *StarsStore) GetBalance(_ context.Context, userID int64) (domain.StarsBalance, error) {
|
||||
if userID == 0 {
|
||||
return domain.StarsBalance{}, nil
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
st := s.states[userID]
|
||||
if st == nil {
|
||||
return domain.StarsBalance{UserID: userID}, nil
|
||||
}
|
||||
return domain.StarsBalance{UserID: userID, Balance: st.balance, Granted: st.granted}, nil
|
||||
}
|
||||
|
||||
func (s *StarsStore) EnsureGrant(_ context.Context, userID, amount int64, date int) (domain.StarsBalance, bool, error) {
|
||||
if userID == 0 {
|
||||
return domain.StarsBalance{}, false, nil
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
st := s.states[userID]
|
||||
if st == nil {
|
||||
st = &starsState{}
|
||||
s.states[userID] = st
|
||||
}
|
||||
if amount <= 0 {
|
||||
return domain.StarsBalance{UserID: userID, Balance: st.balance, Granted: st.granted}, false, nil
|
||||
}
|
||||
if st.granted {
|
||||
return domain.StarsBalance{UserID: userID, Balance: st.balance, Granted: true}, false, nil
|
||||
}
|
||||
st.balance += amount
|
||||
st.granted = true
|
||||
s.appendTxn(st, userID, amount, domain.StarsReasonGrant, domain.Peer{}, date, "", "")
|
||||
return domain.StarsBalance{UserID: userID, Balance: st.balance, Granted: true}, true, nil
|
||||
}
|
||||
|
||||
func (s *StarsStore) Credit(_ context.Context, userID, amount int64, reason domain.StarsTransactionReason, peer domain.Peer, date int, title, desc string) (domain.StarsBalance, error) {
|
||||
if userID == 0 || amount <= 0 {
|
||||
return domain.StarsBalance{}, domain.ErrStarsInvalidAmount
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
st := s.states[userID]
|
||||
if st == nil {
|
||||
st = &starsState{}
|
||||
s.states[userID] = st
|
||||
}
|
||||
st.balance += amount
|
||||
s.appendTxn(st, userID, amount, reason, peer, date, title, desc)
|
||||
return domain.StarsBalance{UserID: userID, Balance: st.balance, Granted: st.granted}, nil
|
||||
}
|
||||
|
||||
func (s *StarsStore) Debit(_ context.Context, userID, amount int64, reason domain.StarsTransactionReason, peer domain.Peer, date int, title, desc string) (domain.StarsBalance, error) {
|
||||
if userID == 0 || amount <= 0 {
|
||||
return domain.StarsBalance{}, domain.ErrStarsInvalidAmount
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
st := s.states[userID]
|
||||
if st == nil || st.balance < amount {
|
||||
return domain.StarsBalance{}, domain.ErrStarsInsufficient
|
||||
}
|
||||
st.balance -= amount
|
||||
s.appendTxn(st, userID, -amount, reason, peer, date, title, desc)
|
||||
return domain.StarsBalance{UserID: userID, Balance: st.balance, Granted: st.granted}, nil
|
||||
}
|
||||
|
||||
func (s *StarsStore) ListTransactions(_ context.Context, userID int64, offset string, limit int) (domain.StarsTransactionPage, error) {
|
||||
if userID == 0 {
|
||||
return domain.StarsTransactionPage{}, nil
|
||||
}
|
||||
if limit <= 0 || limit > domain.MaxStarsTransactionsLimit {
|
||||
limit = domain.MaxStarsTransactionsLimit
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
st := s.states[userID]
|
||||
if st == nil {
|
||||
return domain.StarsTransactionPage{}, nil
|
||||
}
|
||||
page := domain.StarsTransactionPage{Balance: st.balance}
|
||||
cursor, hasCursor := domain.DecodeStarsCursor(offset)
|
||||
// 倒序遍历(id DESC)。
|
||||
out := make([]domain.StarsTransaction, 0, limit)
|
||||
for i := len(st.txns) - 1; i >= 0; i-- {
|
||||
t := st.txns[i]
|
||||
if hasCursor && t.ID >= cursor {
|
||||
continue
|
||||
}
|
||||
out = append(out, t)
|
||||
if len(out) == limit {
|
||||
// 还有更早的流水则给出下一页游标。
|
||||
if i-1 >= 0 {
|
||||
page.NextOffset = domain.EncodeStarsCursor(t.ID)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
page.Transactions = out
|
||||
return page, nil
|
||||
}
|
||||
|
||||
func (s *StarsStore) appendTxn(st *starsState, userID, amount int64, reason domain.StarsTransactionReason, peer domain.Peer, date int, title, desc string) {
|
||||
s.nextID++
|
||||
st.txns = append(st.txns, domain.StarsTransaction{
|
||||
ID: s.nextID,
|
||||
UserID: userID,
|
||||
Peer: peer,
|
||||
Amount: amount,
|
||||
Date: date,
|
||||
Reason: reason,
|
||||
Title: title,
|
||||
Description: desc,
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue