flag too many outcomes

This commit is contained in:
Asher Samuel 2025-07-14 19:30:37 +03:00
parent ae56d253c2
commit e3545f3f8c
17 changed files with 227 additions and 30 deletions

View File

@ -79,4 +79,6 @@ DROP TABLE IF EXISTS events;
DROP TABLE IF EXISTS leagues;
DROP TABLE IF EXISTS teams;
DROP TABLE IF EXISTS settings;
DROP TABLE IF EXISTS bonus;
DROP TABLE IF EXISTS flags;
-- DELETE FROM wallet_transfer;

View File

@ -208,7 +208,7 @@ CREATE TABLE IF NOT EXISTS branches (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
location TEXT NOT NULL,
profit_percent REAL NOt NULL,
profit_percent REAL NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT false,
wallet_id BIGINT NOT NULL,
branch_manager_id BIGINT NOT NULL,
@ -319,6 +319,21 @@ CREATE TABLE bonus (
multiplier REAL NOT NULL,
balance_cap BIGINT NOT NULL DEFAULT 0
);
CREATE TABLE flags (
id BIGSERIAL PRIMARY KEY,
bet_id BIGINT REFERENCES bets(id) ON DELETE CASCADE,
odd_id BIGINT REFERENCES odds(id),
reason TEXT,
flagged_at TIMESTAMP DEFAULT NOW(),
resolved BOOLEAN DEFAULT FALSE,
-- either bet or odd is flagged (not at the same time)
CHECK (
(bet_id IS NOT NULL AND odd_id IS NULL)
OR
(bet_id IS NULL AND odd_id IS NOT NULL)
)
);
-- Views
CREATE VIEW companies_details AS
SELECT companies.*,

View File

@ -4,7 +4,7 @@ VALUES ('max_number_of_outcomes', '30'),
('bet_amount_limit', '100000'),
('daily_ticket_limit', '50'),
('total_winnings_limit', '1000000'),
('amount_for_bet_referral', '1000000')
('amount_for_bet_referral', '1000000'),
('cashback_amount_cap', '1000')
ON CONFLICT (key) DO
UPDATE

View File

@ -101,7 +101,7 @@ WHERE (event_id = $1)
SELECT *
FROM bet_outcomes
WHERE bet_id = $1;
-- name: GetBetCount :one
-- name: GetBetCountByUserID :one
SELECT COUNT(*)
FROM bets
WHERE user_id = $1

8
db/query/flags.sql Normal file
View File

@ -0,0 +1,8 @@
-- name: CreateFlag :one
INSERT INTO flags (
bet_id,
odd_id,
reason
) VALUES (
$1, $2, $3
) RETURNING *;

View File

@ -282,20 +282,20 @@ func (q *Queries) GetBetByUserID(ctx context.Context, userID int64) ([]BetWithOu
return items, nil
}
const GetBetCount = `-- name: GetBetCount :one
const GetBetCountByUserID = `-- name: GetBetCountByUserID :one
SELECT COUNT(*)
FROM bets
WHERE user_id = $1
AND outcomes_hash = $2
`
type GetBetCountParams struct {
type GetBetCountByUserIDParams struct {
UserID int64 `json:"user_id"`
OutcomesHash string `json:"outcomes_hash"`
}
func (q *Queries) GetBetCount(ctx context.Context, arg GetBetCountParams) (int64, error) {
row := q.db.QueryRow(ctx, GetBetCount, arg.UserID, arg.OutcomesHash)
func (q *Queries) GetBetCountByUserID(ctx context.Context, arg GetBetCountByUserIDParams) (int64, error) {
row := q.db.QueryRow(ctx, GetBetCountByUserID, arg.UserID, arg.OutcomesHash)
var count int64
err := row.Scan(&count)
return count, err

42
gen/db/flags.sql.go Normal file
View File

@ -0,0 +1,42 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: flags.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateFlag = `-- name: CreateFlag :one
INSERT INTO flags (
bet_id,
odd_id,
reason
) VALUES (
$1, $2, $3
) RETURNING id, bet_id, odd_id, reason, flagged_at, resolved
`
type CreateFlagParams struct {
BetID pgtype.Int8 `json:"bet_id"`
OddID pgtype.Int8 `json:"odd_id"`
Reason pgtype.Text `json:"reason"`
}
func (q *Queries) CreateFlag(ctx context.Context, arg CreateFlagParams) (Flag, error) {
row := q.db.QueryRow(ctx, CreateFlag, arg.BetID, arg.OddID, arg.Reason)
var i Flag
err := row.Scan(
&i.ID,
&i.BetID,
&i.OddID,
&i.Reason,
&i.FlaggedAt,
&i.Resolved,
)
return i, err
}

View File

@ -276,6 +276,15 @@ type FavoriteGame struct {
CreatedAt pgtype.Timestamp `json:"created_at"`
}
type Flag struct {
ID int64 `json:"id"`
BetID pgtype.Int8 `json:"bet_id"`
OddID pgtype.Int8 `json:"odd_id"`
Reason pgtype.Text `json:"reason"`
FlaggedAt pgtype.Timestamp `json:"flagged_at"`
Resolved pgtype.Bool `json:"resolved"`
}
type League struct {
ID int64 `json:"id"`
Name string `json:"name"`

View File

@ -61,6 +61,15 @@ type BetFilter struct {
CreatedAfter ValidTime
}
type Flag struct {
ID int64
BetID int64
OddID int64
Reason string
FlaggedAt time.Time
Resolved bool
}
type GetBet struct {
ID int64
Amount Currency
@ -104,6 +113,12 @@ type CreateBetWithFastCodeReq struct {
BranchID *int64 `json:"branch_id"`
}
type CreateFlagReq struct {
BetID int64
OddID int64
Reason string
}
type RandomBetReq struct {
BranchID int64 `json:"branch_id" validate:"required" example:"1"`
NumberOfBets int64 `json:"number_of_bets" validate:"required" example:"1"`

View File

@ -76,6 +76,17 @@ func convertDBBetWithOutcomes(bet dbgen.BetWithOutcome) domain.GetBet {
}
}
func convertDBFlag(flag dbgen.Flag) domain.Flag {
return domain.Flag{
ID: flag.ID,
BetID: flag.BetID.Int64,
OddID: flag.OddID.Int64,
Reason: flag.Reason.String,
FlaggedAt: flag.FlaggedAt.Time,
Resolved: flag.Resolved.Bool,
}
}
func convertDBCreateBetOutcome(betOutcome domain.CreateBetOutcome) dbgen.CreateBetOutcomeParams {
return dbgen.CreateBetOutcomeParams{
BetID: betOutcome.BetID,
@ -140,6 +151,35 @@ func (s *Store) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBe
return rows, nil
}
func (s *Store) CreateFlag(ctx context.Context, flag domain.CreateFlagReq) (domain.Flag, error) {
createFlag := dbgen.CreateFlagParams{
BetID: pgtype.Int8{
Int64: flag.BetID,
Valid: flag.BetID != 0,
},
OddID: pgtype.Int8{
Int64: flag.OddID,
Valid: flag.OddID != 0,
},
Reason: pgtype.Text{
String: flag.Reason,
Valid: true,
},
}
f, err := s.queries.CreateFlag(ctx, createFlag)
if err != nil {
domain.MongoDBLogger.Error("failed to create flag",
zap.String("flag", f.Reason.String),
zap.Any("flag_id", f.ID),
zap.Error(err),
)
return domain.Flag{}, err
}
return convertDBFlag(f), nil
}
func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) {
bet, err := s.queries.GetBetByID(ctx, id)
if err != nil {
@ -237,8 +277,8 @@ func (s *Store) GetBetsForCashback(ctx context.Context) ([]domain.GetBet, error)
return res, nil
}
func (s *Store) GetBetCount(ctx context.Context, UserID int64, outcomesHash string) (int64, error) {
count, err := s.queries.GetBetCount(ctx, dbgen.GetBetCountParams{
func (s *Store) GetBetCountByUserID(ctx context.Context, UserID int64, outcomesHash string) (int64, error) {
count, err := s.queries.GetBetCountByUserID(ctx, dbgen.GetBetCountByUserIDParams{
UserID: UserID,
OutcomesHash: outcomesHash,
})

View File

@ -10,13 +10,14 @@ import (
type BetStore interface {
CreateBet(ctx context.Context, bet domain.CreateBet) (domain.Bet, error)
CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBetOutcome) (int64, error)
CreateFlag(ctx context.Context, flag domain.CreateFlagReq) (domain.Flag, error)
GetBetByID(ctx context.Context, id int64) (domain.GetBet, error)
GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, error)
GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error)
GetBetByFastCode(ctx context.Context, fastcode string) (domain.GetBet, error)
GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error)
GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error)
GetBetCount(ctx context.Context, userID int64, outcomesHash string) (int64, error)
GetBetCountByUserID(ctx context.Context, userID int64, outcomesHash string) (int64, error)
UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error
UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error
UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)

View File

@ -269,7 +269,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
return domain.CreateBetRes{}, err
}
count, err := s.GetBetCount(ctx, userID, outcomesHash)
count, err := s.GetBetCountByUserID(ctx, userID, outcomesHash)
if err != nil {
s.mongoLogger.Error("failed to generate cashout ID",
zap.Int64("user_id", userID),
@ -360,15 +360,15 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
case domain.RoleCustomer:
// Only the customer is able to create a online bet
newBet.IsShopBet = false
err = s.DeductBetFromCustomerWallet(ctx, req.Amount, userID)
if err != nil {
s.mongoLogger.Error("customer wallet deduction failed",
zap.Float32("amount", req.Amount),
zap.Int64("user_id", userID),
zap.Error(err),
)
return domain.CreateBetRes{}, err
}
// err = s.DeductBetFromCustomerWallet(ctx, req.Amount, userID)
// if err != nil {
// s.mongoLogger.Error("customer wallet deduction failed",
// zap.Float32("amount", req.Amount),
// zap.Int64("user_id", userID),
// zap.Error(err),
// )
// return domain.CreateBetRes{}, err
// }
default:
s.mongoLogger.Error("unknown role type",
zap.String("role", string(role)),
@ -398,6 +398,23 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
return domain.CreateBetRes{}, err
}
// flag bets that have more than three outcomes
if len(outcomes) > 3 {
flag := domain.CreateFlagReq{
BetID: bet.ID,
OddID: 0,
Reason: fmt.Sprintf("too many outcomes - (%d)", len(outcomes)),
}
_, err := s.betStore.CreateFlag(ctx, flag)
if err != nil {
s.mongoLogger.Error("failed to create flag for bet",
zap.Int64("bet_id", bet.ID),
zap.Error(err),
)
}
}
res := domain.ConvertCreateBet(bet, rows)
return res, nil
@ -716,7 +733,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le
return domain.CreateBetRes{}, err
}
count, err := s.GetBetCount(ctx, userID, outcomesHash)
count, err := s.GetBetCountByUserID(ctx, userID, outcomesHash)
if err != nil {
s.mongoLogger.Error("failed to get bet count",
zap.Int64("user_id", userID),
@ -799,8 +816,8 @@ func (s *Service) GetBetByFastCode(ctx context.Context, fastcode string) (domain
return s.betStore.GetBetByFastCode(ctx, fastcode)
}
func (s *Service) GetBetCount(ctx context.Context, UserID int64, outcomesHash string) (int64, error) {
return s.betStore.GetBetCount(ctx, UserID, outcomesHash)
func (s *Service) GetBetCountByUserID(ctx context.Context, UserID int64, outcomesHash string) (int64, error) {
return s.betStore.GetBetCountByUserID(ctx, UserID, outcomesHash)
}
func (s *Service) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error {

View File

@ -0,0 +1,16 @@
Sports Betting Reports (Periodic)
Period,Total Bets,Total Cash Made,Total Cash Out,Total Cash Backs,Total Deposits,Total Withdrawals,Total Tickets
5min,0,0.00,0.00,0.00,0.00,0.00,0
Virtual Game Reports (Periodic)
Game Name,Number of Bets,Total Transaction Sum
Company Reports (Periodic)
Company ID,Company Name,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
Branch Reports (Periodic)
Branch ID,Branch Name,Company ID,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
Total Summary
Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
0,0.00,0.00,0.00
1 Sports Betting Reports (Periodic)
2 Period,Total Bets,Total Cash Made,Total Cash Out,Total Cash Backs,Total Deposits,Total Withdrawals,Total Tickets
3 5min,0,0.00,0.00,0.00,0.00,0.00,0
4 Virtual Game Reports (Periodic)
5 Game Name,Number of Bets,Total Transaction Sum
6 Company Reports (Periodic)
7 Company ID,Company Name,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
8 Branch Reports (Periodic)
9 Branch ID,Branch Name,Company ID,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
10 Total Summary
11 Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
12 0,0.00,0.00,0.00

View File

@ -0,0 +1,16 @@
Sports Betting Reports (Periodic)
Period,Total Bets,Total Cash Made,Total Cash Out,Total Cash Backs,Total Deposits,Total Withdrawals,Total Tickets
5min,0,0.00,0.00,0.00,0.00,0.00,0
Virtual Game Reports (Periodic)
Game Name,Number of Bets,Total Transaction Sum
Company Reports (Periodic)
Company ID,Company Name,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
Branch Reports (Periodic)
Branch ID,Branch Name,Company ID,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
Total Summary
Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
0,0.00,0.00,0.00
1 Sports Betting Reports (Periodic)
2 Period,Total Bets,Total Cash Made,Total Cash Out,Total Cash Backs,Total Deposits,Total Withdrawals,Total Tickets
3 5min,0,0.00,0.00,0.00,0.00,0.00,0
4 Virtual Game Reports (Periodic)
5 Game Name,Number of Bets,Total Transaction Sum
6 Company Reports (Periodic)
7 Company ID,Company Name,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
8 Branch Reports (Periodic)
9 Branch ID,Branch Name,Company ID,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
10 Total Summary
11 Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
12 0,0.00,0.00,0.00

View File

@ -0,0 +1,16 @@
Sports Betting Reports (Periodic)
Period,Total Bets,Total Cash Made,Total Cash Out,Total Cash Backs,Total Deposits,Total Withdrawals,Total Tickets
5min,0,0.00,0.00,0.00,0.00,0.00,0
Virtual Game Reports (Periodic)
Game Name,Number of Bets,Total Transaction Sum
Company Reports (Periodic)
Company ID,Company Name,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
Branch Reports (Periodic)
Branch ID,Branch Name,Company ID,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
Total Summary
Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
0,0.00,0.00,0.00
1 Sports Betting Reports (Periodic)
2 Period,Total Bets,Total Cash Made,Total Cash Out,Total Cash Backs,Total Deposits,Total Withdrawals,Total Tickets
3 5min,0,0.00,0.00,0.00,0.00,0.00,0
4 Virtual Game Reports (Periodic)
5 Game Name,Number of Bets,Total Transaction Sum
6 Company Reports (Periodic)
7 Company ID,Company Name,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
8 Branch Reports (Periodic)
9 Branch ID,Branch Name,Company ID,Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
10 Total Summary
11 Total Bets,Total Cash In,Total Cash Out,Total Cash Backs
12 0,0.00,0.00,0.00