fix(stars): sync filter transaction history by direction
This commit is contained in:
parent
94f3843d23
commit
3a123f38db
19 changed files with 559 additions and 135 deletions
|
|
@ -1159,7 +1159,7 @@ type GiftsService interface {
|
|||
SetNotifications(ctx context.Context, userID, channelID int64, enabled bool) error
|
||||
Withdraw(ctx context.Context, req domain.StarGiftWithdrawalRequest) (domain.StarGiftWithdrawal, error)
|
||||
TonBalance(ctx context.Context, userID int64) (int64, error)
|
||||
TonTransactions(ctx context.Context, userID int64, offset string, limit int) (domain.TonTransactionPage, error)
|
||||
TonTransactions(ctx context.Context, userID int64, query domain.StarsTransactionQuery) (domain.TonTransactionPage, error)
|
||||
IssuePurchaseForm(ctx context.Context, form domain.StarGiftPurchaseForm) (domain.StarGiftPurchaseForm, error)
|
||||
ValidatePurchaseForm(ctx context.Context, req domain.StarGiftPurchaseRequest) error
|
||||
Purchase(ctx context.Context, req domain.StarGiftPurchaseRequest) (domain.StarGiftPurchaseResult, error)
|
||||
|
|
@ -1172,7 +1172,7 @@ type StarsService interface {
|
|||
GetBalance(ctx context.Context, userID int64) (domain.StarsBalance, error)
|
||||
Credit(ctx context.Context, userID, amount int64, reason domain.StarsTransactionReason, peer domain.Peer, title, desc string) (domain.StarsBalance, error)
|
||||
Debit(ctx context.Context, userID, amount int64, reason domain.StarsTransactionReason, peer domain.Peer, title, desc string) (domain.StarsBalance, error)
|
||||
ListTransactions(ctx context.Context, userID int64, offset string, limit int) (domain.StarsTransactionPage, error)
|
||||
ListTransactions(ctx context.Context, userID int64, query domain.StarsTransactionQuery) (domain.StarsTransactionPage, error)
|
||||
}
|
||||
|
||||
// SecretChatService 抽象私聊端对端加密(Secret Chat)握手状态机(app/secretchat)。
|
||||
|
|
|
|||
|
|
@ -152,6 +152,8 @@ func balanceTooLowErr() error { return tgerr.New(400, "BALANCE_TOO_LOW") }
|
|||
|
||||
func starsAmountInvalidErr() error { return tgerr.New(400, "STARS_AMOUNT_INVALID") }
|
||||
|
||||
func subscriptionIDInvalidErr() error { return tgerr.New(400, "SUBSCRIPTION_ID_INVALID") }
|
||||
|
||||
func starsFormAmountMismatchErr() error { return tgerr.New(406, "STARS_FORM_AMOUNT_MISMATCH") }
|
||||
|
||||
func formIDEmptyErr() error { return tgerr.New(400, "FORM_ID_EMPTY") }
|
||||
|
|
|
|||
|
|
@ -203,9 +203,9 @@ func (r *Router) onPaymentsGetStarsRevenueStats(ctx context.Context, req *tg.Pay
|
|||
|
||||
type channelGiftLedgerReader interface {
|
||||
ChannelStarsBalance(ctx context.Context, channelID int64) (int64, error)
|
||||
ChannelStarsTransactions(ctx context.Context, channelID int64, offset string, limit int) (domain.StarsTransactionPage, error)
|
||||
ChannelStarsTransactions(ctx context.Context, channelID int64, query domain.StarsTransactionQuery) (domain.StarsTransactionPage, error)
|
||||
ChannelTonBalance(ctx context.Context, channelID int64) (int64, error)
|
||||
ChannelTonTransactions(ctx context.Context, channelID int64, offset string, limit int) (domain.TonTransactionPage, error)
|
||||
ChannelTonTransactions(ctx context.Context, channelID int64, query domain.StarsTransactionQuery) (domain.TonTransactionPage, error)
|
||||
}
|
||||
|
||||
// onPaymentsGetStarsStatus 返回请求 peer 的 Stars/本地 TON 余额。个人与频道账本
|
||||
|
|
@ -270,12 +270,9 @@ func (r *Router) onPaymentsGetStarsTransactions(ctx context.Context, req *tg.Pay
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset, limit := "", domain.MaxStarsTransactionsLimit
|
||||
if req != nil {
|
||||
offset = req.Offset
|
||||
if req.Limit > 0 {
|
||||
limit = req.Limit
|
||||
}
|
||||
query, err := starsTransactionQuery(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ton := req != nil && req.GetTon()
|
||||
if owner.Type == domain.PeerTypeChannel {
|
||||
|
|
@ -287,7 +284,7 @@ func (r *Router) onPaymentsGetStarsTransactions(ctx context.Context, req *tg.Pay
|
|||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
if ton {
|
||||
page, err := ledger.ChannelTonTransactions(ctx, owner.ID, offset, limit)
|
||||
page, err := ledger.ChannelTonTransactions(ctx, owner.ID, query)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
|
|
@ -301,7 +298,7 @@ func (r *Router) onPaymentsGetStarsTransactions(ctx context.Context, req *tg.Pay
|
|||
r.enrichChannelTonLedgerStatus(ctx, userID, owner.ID, page.Transactions, out)
|
||||
return out, nil
|
||||
}
|
||||
page, err := ledger.ChannelStarsTransactions(ctx, owner.ID, offset, limit)
|
||||
page, err := ledger.ChannelStarsTransactions(ctx, owner.ID, query)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
|
|
@ -319,7 +316,7 @@ func (r *Router) onPaymentsGetStarsTransactions(ctx context.Context, req *tg.Pay
|
|||
if r.deps.Gifts == nil {
|
||||
return emptyStarsStatus(&tg.StarsTonAmount{}), nil
|
||||
}
|
||||
page, err := r.deps.Gifts.TonTransactions(ctx, userID, offset, limit)
|
||||
page, err := r.deps.Gifts.TonTransactions(ctx, userID, query)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
|
|
@ -342,7 +339,7 @@ func (r *Router) onPaymentsGetStarsTransactions(ctx context.Context, req *tg.Pay
|
|||
if r.deps.Stars == nil {
|
||||
return emptyStarsStatus(&tg.StarsAmount{}), nil
|
||||
}
|
||||
page, err := r.deps.Stars.ListTransactions(ctx, userID, offset, limit)
|
||||
page, err := r.deps.Stars.ListTransactions(ctx, userID, query)
|
||||
if err != nil {
|
||||
return nil, starsErr(err)
|
||||
}
|
||||
|
|
@ -360,6 +357,37 @@ func (r *Router) onPaymentsGetStarsTransactions(ctx context.Context, req *tg.Pay
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func starsTransactionQuery(req *tg.PaymentsGetStarsTransactionsRequest) (domain.StarsTransactionQuery, error) {
|
||||
if req == nil {
|
||||
return domain.StarsTransactionQuery{}, inputRequestInvalidErr()
|
||||
}
|
||||
inbound, outbound := req.GetInbound(), req.GetOutbound()
|
||||
if inbound && outbound {
|
||||
return domain.StarsTransactionQuery{}, inputRequestInvalidErr()
|
||||
}
|
||||
if _, ok := req.GetSubscriptionID(); ok {
|
||||
// Stars subscriptions are not part of the current business model. Do not
|
||||
// silently return the unfiltered ledger for a requested subscription.
|
||||
return domain.StarsTransactionQuery{}, subscriptionIDInvalidErr()
|
||||
}
|
||||
direction := domain.StarsTransactionDirectionAll
|
||||
if inbound {
|
||||
direction = domain.StarsTransactionDirectionIncoming
|
||||
} else if outbound {
|
||||
direction = domain.StarsTransactionDirectionOutgoing
|
||||
}
|
||||
limit := req.Limit
|
||||
if limit <= 0 || limit > domain.MaxStarsTransactionsLimit {
|
||||
limit = domain.MaxStarsTransactionsLimit
|
||||
}
|
||||
return domain.StarsTransactionQuery{
|
||||
Offset: req.Offset,
|
||||
Limit: limit,
|
||||
Direction: direction,
|
||||
Ascending: req.GetAscending(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Router) starGiftLedgerOwner(ctx context.Context, req *tg.PaymentsGetStarsStatusRequest) (int64, domain.Peer, error) {
|
||||
if req == nil {
|
||||
return 0, domain.Peer{}, peerIDInvalidErr()
|
||||
|
|
|
|||
|
|
@ -1598,7 +1598,7 @@ func TestStarsTopupInvoiceFallbackCreditsBalance(t *testing.T) {
|
|||
if bal, _ := r.deps.Stars.GetBalance(ctx, sender.ID); bal.Balance != 3500 {
|
||||
t.Fatalf("balance after topup = %d, want 3500", bal.Balance)
|
||||
}
|
||||
page, err := r.deps.Stars.ListTransactions(ctx, sender.ID, "", 10)
|
||||
page, err := r.deps.Stars.ListTransactions(ctx, sender.ID, domain.StarsTransactionQuery{Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("list transactions: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,77 @@ func TestOnPaymentsGetStarsTransactions(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestOnPaymentsGetStarsTransactionsDirections(t *testing.T) {
|
||||
const userID int64 = 1000000001
|
||||
svc := appstars.NewService(memory.NewStarsStore(), appstars.WithStartingGrant(0))
|
||||
ctx := WithUserID(context.Background(), userID)
|
||||
if _, err := svc.Credit(ctx, userID, 100, domain.StarsReasonTopup, domain.Peer{}, "", ""); err != nil {
|
||||
t.Fatalf("credit 100: %v", err)
|
||||
}
|
||||
if _, err := svc.Debit(ctx, userID, 40, domain.StarsReasonGift, domain.Peer{}, "", ""); err != nil {
|
||||
t.Fatalf("debit 40: %v", err)
|
||||
}
|
||||
if _, err := svc.Credit(ctx, userID, 20, domain.StarsReasonGift, domain.Peer{}, "", ""); err != nil {
|
||||
t.Fatalf("credit 20: %v", err)
|
||||
}
|
||||
if _, err := svc.Debit(ctx, userID, 10, domain.StarsReasonReaction, domain.Peer{}, "", ""); err != nil {
|
||||
t.Fatalf("debit 10: %v", err)
|
||||
}
|
||||
r := New(Config{}, Deps{Stars: svc}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
all := &tg.PaymentsGetStarsTransactionsRequest{Peer: &tg.InputPeerSelf{}, Limit: 50}
|
||||
assertRPCStarsAmounts(t, r, ctx, all, []int64{-10, 20, -40, 100})
|
||||
|
||||
incoming := &tg.PaymentsGetStarsTransactionsRequest{Peer: &tg.InputPeerSelf{}, Limit: 50}
|
||||
incoming.SetInbound(true)
|
||||
assertRPCStarsAmounts(t, r, ctx, incoming, []int64{20, 100})
|
||||
|
||||
outgoing := &tg.PaymentsGetStarsTransactionsRequest{Peer: &tg.InputPeerSelf{}, Limit: 50}
|
||||
outgoing.SetOutbound(true)
|
||||
assertRPCStarsAmounts(t, r, ctx, outgoing, []int64{-10, -40})
|
||||
|
||||
ascending := &tg.PaymentsGetStarsTransactionsRequest{Peer: &tg.InputPeerSelf{}, Limit: 50}
|
||||
ascending.SetInbound(true)
|
||||
ascending.SetAscending(true)
|
||||
assertRPCStarsAmounts(t, r, ctx, ascending, []int64{100, 20})
|
||||
}
|
||||
|
||||
func TestOnPaymentsGetStarsTransactionsRejectsInvalidFilters(t *testing.T) {
|
||||
r := starsRouter(t, 1000)
|
||||
ctx := WithUserID(context.Background(), 1000000001)
|
||||
|
||||
both := &tg.PaymentsGetStarsTransactionsRequest{Peer: &tg.InputPeerSelf{}}
|
||||
both.SetInbound(true)
|
||||
both.SetOutbound(true)
|
||||
if _, err := r.onPaymentsGetStarsTransactions(ctx, both); err == nil {
|
||||
t.Fatal("mutually exclusive inbound/outbound unexpectedly succeeded")
|
||||
}
|
||||
|
||||
subscription := &tg.PaymentsGetStarsTransactionsRequest{Peer: &tg.InputPeerSelf{}}
|
||||
subscription.SetSubscriptionID("subscription-1")
|
||||
if _, err := r.onPaymentsGetStarsTransactions(ctx, subscription); err == nil {
|
||||
t.Fatal("unsupported subscription filter unexpectedly returned the unfiltered ledger")
|
||||
}
|
||||
}
|
||||
|
||||
func assertRPCStarsAmounts(t *testing.T, r *Router, ctx context.Context, req *tg.PaymentsGetStarsTransactionsRequest, want []int64) {
|
||||
t.Helper()
|
||||
status, err := r.onPaymentsGetStarsTransactions(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("getStarsTransactions: %v", err)
|
||||
}
|
||||
history, _ := status.GetHistory()
|
||||
if len(history) != len(want) {
|
||||
t.Fatalf("history count = %d, want %d: %+v", len(history), len(want), history)
|
||||
}
|
||||
for i, amount := range want {
|
||||
stars, ok := history[i].Amount.(*tg.StarsAmount)
|
||||
if !ok || stars.Amount != amount {
|
||||
t.Fatalf("history[%d].amount = %#v, want %d", i, history[i].Amount, amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTGStarsTransactionsPaidMessage(t *testing.T) {
|
||||
out := tgStarsTransactions([]domain.StarsTransaction{{
|
||||
ID: 1, UserID: 42, Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 50},
|
||||
|
|
@ -127,7 +198,7 @@ func (s *channelLedgerGifts) ChannelStarsBalance(context.Context, int64) (int64,
|
|||
return s.starsBalance, nil
|
||||
}
|
||||
|
||||
func (s *channelLedgerGifts) ChannelStarsTransactions(context.Context, int64, string, int) (domain.StarsTransactionPage, error) {
|
||||
func (s *channelLedgerGifts) ChannelStarsTransactions(context.Context, int64, domain.StarsTransactionQuery) (domain.StarsTransactionPage, error) {
|
||||
return s.starsPage, nil
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +206,7 @@ func (s *channelLedgerGifts) ChannelTonBalance(context.Context, int64) (int64, e
|
|||
return s.tonBalance, nil
|
||||
}
|
||||
|
||||
func (s *channelLedgerGifts) ChannelTonTransactions(context.Context, int64, string, int) (domain.TonTransactionPage, error) {
|
||||
func (s *channelLedgerGifts) ChannelTonTransactions(context.Context, int64, domain.StarsTransactionQuery) (domain.TonTransactionPage, error) {
|
||||
return s.tonPage, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue