diff --git a/db/query/bet.sql b/db/query/bet.sql index cd3e171..8be3a31 100644 --- a/db/query/bet.sql +++ b/db/query/bet.sql @@ -99,6 +99,11 @@ SELECT * FROM bet_with_outcomes WHERE fast_code = $1 LIMIT 1; +-- name: GetBetsForCashback :many +SELECT * +FROM bet_with_outcomes +WHERE status = 2 + AND processed = false; -- name: GetBetOutcomeByEventID :many SELECT * FROM bet_outcomes diff --git a/gen/db/bet.sql.go b/gen/db/bet.sql.go index 0a92a69..3f93e45 100644 --- a/gen/db/bet.sql.go +++ b/gen/db/bet.sql.go @@ -512,6 +512,52 @@ func (q *Queries) GetBetOutcomeByEventID(ctx context.Context, arg GetBetOutcomeB return items, nil } +const GetBetsForCashback = `-- name: GetBetsForCashback :many +SELECT id, amount, total_odds, status, full_name, phone_number, company_id, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes_hash, fast_code, processed, outcomes +FROM bet_with_outcomes +WHERE status = 2 + AND processed = false +` + +func (q *Queries) GetBetsForCashback(ctx context.Context) ([]BetWithOutcome, error) { + rows, err := q.db.Query(ctx, GetBetsForCashback) + if err != nil { + return nil, err + } + defer rows.Close() + var items []BetWithOutcome + for rows.Next() { + var i BetWithOutcome + if err := rows.Scan( + &i.ID, + &i.Amount, + &i.TotalOdds, + &i.Status, + &i.FullName, + &i.PhoneNumber, + &i.CompanyID, + &i.BranchID, + &i.UserID, + &i.CashedOut, + &i.CashoutID, + &i.CreatedAt, + &i.UpdatedAt, + &i.IsShopBet, + &i.OutcomesHash, + &i.FastCode, + &i.Processed, + &i.Outcomes, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const UpdateBetOutcomeStatus = `-- name: UpdateBetOutcomeStatus :one UPDATE bet_outcomes SET status = $1 diff --git a/internal/repository/bet.go b/internal/repository/bet.go index 765bd27..fe6d019 100644 --- a/internal/repository/bet.go +++ b/internal/repository/bet.go @@ -293,6 +293,22 @@ func (s *Store) GetBetByFastCode(ctx context.Context, fastcode string) (domain.G return convertDBBetWithOutcomes(bet), nil } +func (s *Store) GetBetsForCashback(ctx context.Context) ([]domain.GetBet, error) { + bets, err := s.queries.GetBetsForCashback(ctx) + var res []domain.GetBet + + if err != nil { + return nil, err + } + + for _, bet := range bets { + cashbackBet := convertDBBetWithOutcomes(bet) + res = append(res, cashbackBet) + } + + return res, nil +} + func (s *Store) GetBetCount(ctx context.Context, UserID int64, outcomesHash string) (int64, error) { count, err := s.queries.GetBetCount(ctx, dbgen.GetBetCountParams{ UserID: pgtype.Int8{Int64: UserID, Valid: true}, diff --git a/internal/services/bet/port.go b/internal/services/bet/port.go index 2f29610..dd0d80c 100644 --- a/internal/services/bet/port.go +++ b/internal/services/bet/port.go @@ -45,5 +45,6 @@ type BetStore interface { GetSportDetails(ctx context.Context, filter domain.ReportFilter) (map[string]string, error) GetSportMarketPopularity(ctx context.Context, filter domain.ReportFilter) (map[string]string, error) + GetBetsForCashback(ctx context.Context) ([]domain.GetBet, error) UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error } diff --git a/internal/services/bet/service.go b/internal/services/bet/service.go index 011be12..fbcda23 100644 --- a/internal/services/bet/service.go +++ b/internal/services/bet/service.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "log/slog" + "math" "math/big" random "math/rand" "sort" @@ -908,9 +909,7 @@ func (s *Service) SetBetToRemoved(ctx context.Context, id int64) error { } func (s *Service) ProcessBetCashback(ctx context.Context) error { - // TODO: get filterd data from db instead of filtering it here - // get all bets (status not pending) (processed not true) - bets, err := s.GetAllBets(ctx, domain.BetFilter{}) + bets, err := s.betStore.GetBetsForCashback(ctx) if err != nil { s.mongoLogger.Error("failed to fetch bets", zap.Error(err), @@ -919,35 +918,26 @@ func (s *Service) ProcessBetCashback(ctx context.Context) error { } for _, bet := range bets { - if bet.Status == domain.OUTCOME_STATUS_PENDING { - continue - } - - loseCount := 0 - outcomes, err := s.GetBetOutcomeByBetID(ctx, bet.ID) - - if err != nil { - s.mongoLogger.Info("failed to fetch outcomes for a best", - zap.Int64("betID", bet.ID), - zap.Error(err), - ) - continue - } - - // bet meets criteria shouldProcess := true - for _, outcome := range outcomes { + loseCount := 0 + + for _, outcome := range bet.Outcomes { + // stop if other outcomes exists in bet outcomes + if outcome.Status != domain.OUTCOME_STATUS_LOSS && outcome.Status != domain.OUTCOME_STATUS_WIN { + shouldProcess = false + break + } + if outcome.Status == domain.OUTCOME_STATUS_LOSS { loseCount++ // only process caseback if bet is lost by one if loseCount > 1 { - shouldProcess = false break } } } - if loseCount != 1 || !shouldProcess { + if !shouldProcess || loseCount != 1 { continue } @@ -968,8 +958,10 @@ func (s *Service) ProcessBetCashback(ctx context.Context) error { continue } - cashbackAmount := calculateCashbackAmount(bet.Amount.Float32(), bet.TotalOdds) - _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, domain.ToCurrency(cashbackAmount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, + // TODO: get cashback amount cap (currently 1000) from settings in the db + cashbackAmount := math.Min(10, float64(calculateCashbackAmount(bet.Amount.Float32(), bet.TotalOdds))) + + _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, domain.ToCurrency(float32(cashbackAmount)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("cashback amount of %f added to users static wallet", cashbackAmount)) if err != nil {