fix: refactored bet to remove unnecessary fields

This commit is contained in:
Samuel Tariku 2025-07-09 21:48:36 +03:00
parent 75f2499bb1
commit d1770eceb6
30 changed files with 754 additions and 860 deletions

View File

@ -2,6 +2,7 @@
"cSpell.words": [
"Cashout",
"narg",
"notificationservice",
"sqlc"
]
}

View File

@ -123,7 +123,7 @@ func main() {
companySvc := company.NewService(store)
leagueSvc := league.New(store)
ticketSvc := ticket.NewService(store, eventSvc, *oddsSvc, domain.MongoDBLogger, *settingSvc, notificationSvc)
betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, logger, domain.MongoDBLogger)
betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, *settingSvc, notificationSvc, logger, domain.MongoDBLogger)
resultSvc := result.NewService(store, cfg, logger, *betSvc, *oddsSvc, eventSvc, leagueSvc, notificationSvc)
bonusSvc := bonus.NewService(store)
referalRepo := repository.NewReferralRepository(store)

View File

@ -45,23 +45,13 @@ CREATE TABLE IF NOT EXISTS bets (
amount BIGINT NOT NULL,
total_odds REAL NOT NULL,
status INT NOT NULL,
full_name VARCHAR(255) NOT NULL,
phone_number VARCHAR(255) NOT NULL,
company_id BIGINT,
branch_id BIGINT,
user_id BIGINT,
cashed_out BOOLEAN DEFAULT FALSE NOT NULL,
cashout_id VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
user_id BIGINT NOT NULL,
is_shop_bet BOOLEAN NOT NULL,
cashed_out BOOLEAN NOT NULL DEFAULT false,
outcomes_hash TEXT NOT NULL,
fast_code VARCHAR(10) NOT NULL,
UNIQUE(cashout_id),
CHECK (
user_id IS NOT NULL
OR branch_id IS NOT NULL
)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS tickets (
id BIGSERIAL PRIMARY KEY,
@ -260,7 +250,8 @@ CREATE TABLE events (
is_live BOOLEAN,
status TEXT,
fetched_at TIMESTAMP DEFAULT now(),
source TEXT DEFAULT 'b365api'
source TEXT DEFAULT 'b365api',
flagged BOOLEAN NOT NULL DEFAULT false
);
CREATE TABLE odds (
id SERIAL PRIMARY KEY,
@ -347,10 +338,16 @@ CREATE TABLE IF NOT EXISTS supported_operations (
);
CREATE VIEW bet_with_outcomes AS
SELECT bets.*,
CONCAT(users.first_name, ' ', users.last_name) AS full_name,
users.phone_number,
JSON_AGG(bet_outcomes.*) AS outcomes
FROM bets
LEFT JOIN bet_outcomes ON bets.id = bet_outcomes.bet_id
GROUP BY bets.id;
LEFT JOIN users ON bets.user_id = users.id
GROUP BY bets.id,
users.first_name,
users.last_name,
users.phone_number;
CREATE VIEW ticket_with_outcomes AS
SELECT tickets.*,
JSON_AGG(ticket_outcomes.*) AS outcomes
@ -438,8 +435,7 @@ ADD CONSTRAINT unique_email UNIQUE (email),
ALTER TABLE refresh_tokens
ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id);
ALTER TABLE bets
ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users(id),
ADD CONSTRAINT fk_bets_branches FOREIGN KEY (branch_id) REFERENCES branches(id);
ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users(id);
ALTER TABLE wallets
ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users(id),
ADD COLUMN currency VARCHAR(3) NOT NULL DEFAULT 'ETB';
@ -494,7 +490,7 @@ VALUES (
'Doe',
'john.doe@example.com',
NULL,
crypt('password123', gen_salt('bf'))::bytea,
crypt('password@123', gen_salt('bf'))::bytea,
'customer',
TRUE,
FALSE,

View File

@ -1,12 +1,14 @@
CREATE TABLE IF NOT EXISTS reported_issues (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL,
user_id BIGINT NOT NULL REFERENCES users(id),
user_role VARCHAR(255) NOT NULL,
subject TEXT NOT NULL,
description TEXT NOT NULL,
issue_type TEXT NOT NULL, -- e.g., "deposit", "withdrawal", "bet", "technical"
status TEXT NOT NULL DEFAULT 'pending', -- pending, in_progress, resolved, rejected
issue_type TEXT NOT NULL,
-- e.g., "deposit", "withdrawal", "bet", "technical"
status TEXT NOT NULL DEFAULT 'pending',
-- pending, in_progress, resolved, rejected
metadata JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -3,17 +3,12 @@ INSERT INTO bets (
amount,
total_odds,
status,
full_name,
phone_number,
branch_id,
user_id,
is_shop_bet,
cashout_id,
company_id,
outcomes_hash,
fast_code
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *;
-- name: CreateBetOutcome :copyfrom
INSERT INTO bet_outcomes (
@ -50,14 +45,6 @@ VALUES (
SELECT *
FROM bet_with_outcomes
wHERE (
branch_id = sqlc.narg('branch_id')
OR sqlc.narg('branch_id') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
user_id = sqlc.narg('user_id')
OR sqlc.narg('user_id') IS NULL
)
@ -65,6 +52,10 @@ wHERE (
is_shop_bet = sqlc.narg('is_shop_bet')
OR sqlc.narg('is_shop_bet') IS NULL
)
AND (
cashed_out = sqlc.narg('cashed_out')
OR sqlc.narg('cashed_out') IS NULL
)
AND (
full_name ILIKE '%' || sqlc.narg('query') || '%'
OR phone_number ILIKE '%' || sqlc.narg('query') || '%'
@ -82,14 +73,6 @@ wHERE (
SELECT *
FROM bet_with_outcomes
WHERE id = $1;
-- name: GetBetByCashoutID :one
SELECT *
FROM bet_with_outcomes
WHERE cashout_id = $1;
-- name: GetBetByBranchID :many
SELECT *
FROM bet_with_outcomes
WHERE branch_id = $1;
-- name: GetBetByUserID :many
SELECT *
FROM bet_with_outcomes

View File

@ -126,45 +126,14 @@ SELECT id
FROM events
WHERE is_live = true;
-- name: GetAllUpcomingEvents :many
SELECT id,
sport_id,
match_name,
home_team,
away_team,
home_team_id,
away_team_id,
home_kit_image,
away_kit_image,
league_id,
league_name,
league_cc,
start_time,
is_live,
status,
source,
fetched_at
SELECT *
FROM events
WHERE start_time > now()
AND is_live = false
AND status = 'upcoming'
ORDER BY start_time ASC;
-- name: GetExpiredUpcomingEvents :many
SELECT events.id,
events.sport_id,
events.match_name,
events.home_team,
events.away_team,
events.home_team_id,
events.away_team_id,
events.home_kit_image,
events.away_kit_image,
events.league_id,
events.league_name,
events.start_time,
events.is_live,
events.status,
events.source,
events.fetched_at,
SELECT events.*,
leagues.country_code as league_cc
FROM events
LEFT JOIN leagues ON leagues.id = league_id
@ -201,22 +170,7 @@ WHERE is_live = false
OR sqlc.narg('country_code') IS NULL
);
-- name: GetPaginatedUpcomingEvents :many
SELECT events.id,
events.sport_id,
events.match_name,
events.home_team,
events.away_team,
events.home_team_id,
events.away_team_id,
events.home_kit_image,
events.away_kit_image,
events.league_id,
events.league_name,
events.start_time,
events.is_live,
events.status,
events.source,
events.fetched_at,
SELECT events.*,
leagues.country_code as league_cc
FROM events
LEFT JOIN leagues ON leagues.id = league_id
@ -246,23 +200,7 @@ WHERE start_time > now()
ORDER BY start_time ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetUpcomingByID :one
SELECT id,
sport_id,
match_name,
home_team,
away_team,
home_team_id,
away_team_id,
home_kit_image,
away_kit_image,
league_id,
league_name,
league_cc,
start_time,
is_live,
status,
source,
fetched_at
SELECT *
FROM events
WHERE id = $1
AND is_live = false
@ -271,9 +209,12 @@ LIMIT 1;
-- name: UpdateMatchResult :exec
UPDATE events
SET score = $1,
status = $2,
fetched_at = NOW()
status = $2
WHERE id = $3;
-- name: UpdateFlagged :exec
UPDATE events
SET flagged = $1
WHERE id = $2;
-- name: DeleteEvent :exec
DELETE FROM events
WHERE id = $1;

View File

@ -1,32 +1,37 @@
-- name: CreateReportedIssue :one
INSERT INTO reported_issues (
customer_id, subject, description, issue_type, metadata
) VALUES (
$1, $2, $3, $4, $5
)
user_id,
user_role,
subject,
description,
issue_type,
metadata
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *;
-- name: ListReportedIssues :many
SELECT * FROM reported_issues
SELECT *
FROM reported_issues
ORDER BY created_at DESC
LIMIT $1 OFFSET $2;
-- name: ListReportedIssuesByCustomer :many
SELECT * FROM reported_issues
WHERE customer_id = $1
-- name: ListReportedIssuesByUser :many
SELECT *
FROM reported_issues
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3;
-- name: CountReportedIssues :one
SELECT COUNT(*) FROM reported_issues;
-- name: CountReportedIssuesByCustomer :one
SELECT COUNT(*) FROM reported_issues WHERE customer_id = $1;
SELECT COUNT(*)
FROM reported_issues;
-- name: CountReportedIssuesByUser :one
SELECT COUNT(*)
FROM reported_issues
WHERE user_id = $1;
-- name: UpdateReportedIssueStatus :exec
UPDATE reported_issues
SET status = $2, updated_at = NOW()
SET status = $2,
updated_at = NOW()
WHERE id = $1;
-- name: DeleteReportedIssue :exec
DELETE FROM reported_issues WHERE id = $1;
DELETE FROM reported_issues
WHERE id = $1;

View File

@ -17,28 +17,60 @@ FROM bets
WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
AND status = 5;
-- name: GetCompanyWiseReport :many
SELECT
b.company_id,
c.name AS company_name,
COUNT(*) AS total_bets,
COALESCE(SUM(b.amount), 0) AS total_cash_made,
COALESCE(SUM(CASE WHEN b.cashed_out THEN b.amount ELSE 0 END), 0) AS total_cash_out,
COALESCE(SUM(CASE WHEN b.status = 5 THEN b.amount ELSE 0 END), 0) AS total_cash_backs
FROM bets b
JOIN companies c ON b.company_id = c.id
SELECT b.company_id,
c.name AS company_name,
COUNT(*) AS total_bets,
COALESCE(SUM(b.amount), 0) AS total_cash_made,
COALESCE(
SUM(
CASE
WHEN b.cashed_out THEN b.amount
ELSE 0
END
),
0
) AS total_cash_out,
COALESCE(
SUM(
CASE
WHEN b.status = 5 THEN b.amount
ELSE 0
END
),
0
) AS total_cash_backs
FROM shop_bet_detail b
JOIN companies c ON b.company_id = c.id
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
GROUP BY b.company_id, c.name;
GROUP BY b.company_id,
c.name;
-- name: GetBranchWiseReport :many
SELECT
b.branch_id,
br.name AS branch_name,
br.company_id,
COUNT(*) AS total_bets,
COALESCE(SUM(b.amount), 0) AS total_cash_made,
COALESCE(SUM(CASE WHEN b.cashed_out THEN b.amount ELSE 0 END), 0) AS total_cash_out,
COALESCE(SUM(CASE WHEN b.status = 5 THEN b.amount ELSE 0 END), 0) AS total_cash_backs
FROM bets b
JOIN branches br ON b.branch_id = br.id
SELECT b.branch_id,
br.name AS branch_name,
br.company_id,
COUNT(*) AS total_bets,
COALESCE(SUM(b.amount), 0) AS total_cash_made,
COALESCE(
SUM(
CASE
WHEN b.cashed_out THEN b.amount
ELSE 0
END
),
0
) AS total_cash_out,
COALESCE(
SUM(
CASE
WHEN b.status = 5 THEN b.amount
ELSE 0
END
),
0
) AS total_cash_backs
FROM shop_bet_detail b
JOIN branches br ON b.branch_id = br.id
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
GROUP BY b.branch_id, br.name, br.company_id;
GROUP BY b.branch_id,
br.name,
br.company_id;

View File

@ -16,33 +16,23 @@ INSERT INTO bets (
amount,
total_odds,
status,
full_name,
phone_number,
branch_id,
user_id,
is_shop_bet,
cashout_id,
company_id,
outcomes_hash,
fast_code
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
RETURNING 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
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, created_at, updated_at
`
type CreateBetParams struct {
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
Status int32 `json:"status"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
BranchID pgtype.Int8 `json:"branch_id"`
UserID pgtype.Int8 `json:"user_id"`
IsShopBet bool `json:"is_shop_bet"`
CashoutID string `json:"cashout_id"`
CompanyID pgtype.Int8 `json:"company_id"`
OutcomesHash string `json:"outcomes_hash"`
FastCode string `json:"fast_code"`
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
Status int32 `json:"status"`
UserID int64 `json:"user_id"`
IsShopBet bool `json:"is_shop_bet"`
OutcomesHash string `json:"outcomes_hash"`
FastCode string `json:"fast_code"`
}
func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, error) {
@ -50,13 +40,8 @@ func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, erro
arg.Amount,
arg.TotalOdds,
arg.Status,
arg.FullName,
arg.PhoneNumber,
arg.BranchID,
arg.UserID,
arg.IsShopBet,
arg.CashoutID,
arg.CompanyID,
arg.OutcomesHash,
arg.FastCode,
)
@ -66,18 +51,13 @@ func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, erro
&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.CashedOut,
&i.OutcomesHash,
&i.FastCode,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
@ -119,44 +99,39 @@ func (q *Queries) DeleteBetOutcome(ctx context.Context, betID int64) error {
}
const GetAllBets = `-- name: GetAllBets :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, outcomes
SELECT id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, created_at, updated_at, full_name, phone_number, outcomes
FROM bet_with_outcomes
wHERE (
branch_id = $1
user_id = $1
OR $1 IS NULL
)
AND (
company_id = $2
is_shop_bet = $2
OR $2 IS NULL
)
AND (
user_id = $3
cashed_out = $3
OR $3 IS NULL
)
AND (
is_shop_bet = $4
full_name ILIKE '%' || $4 || '%'
OR phone_number ILIKE '%' || $4 || '%'
OR $4 IS NULL
)
AND (
full_name ILIKE '%' || $5 || '%'
OR phone_number ILIKE '%' || $5 || '%'
created_at > $5
OR $5 IS NULL
)
AND (
created_at > $6
created_at < $6
OR $6 IS NULL
)
AND (
created_at < $7
OR $7 IS NULL
)
`
type GetAllBetsParams struct {
BranchID pgtype.Int8 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
UserID pgtype.Int8 `json:"user_id"`
IsShopBet pgtype.Bool `json:"is_shop_bet"`
CashedOut pgtype.Bool `json:"cashed_out"`
Query pgtype.Text `json:"query"`
CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"`
@ -164,10 +139,9 @@ type GetAllBetsParams struct {
func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWithOutcome, error) {
rows, err := q.db.Query(ctx, GetAllBets,
arg.BranchID,
arg.CompanyID,
arg.UserID,
arg.IsShopBet,
arg.CashedOut,
arg.Query,
arg.CreatedBefore,
arg.CreatedAfter,
@ -184,18 +158,15 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
&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.CashedOut,
&i.OutcomesHash,
&i.FastCode,
&i.CreatedAt,
&i.UpdatedAt,
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
); err != nil {
return nil, err
@ -208,83 +179,8 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
return items, nil
}
const GetBetByBranchID = `-- name: GetBetByBranchID :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, outcomes
FROM bet_with_outcomes
WHERE branch_id = $1
`
func (q *Queries) GetBetByBranchID(ctx context.Context, branchID pgtype.Int8) ([]BetWithOutcome, error) {
rows, err := q.db.Query(ctx, GetBetByBranchID, branchID)
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.Outcomes,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetBetByCashoutID = `-- name: GetBetByCashoutID :one
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, outcomes
FROM bet_with_outcomes
WHERE cashout_id = $1
`
func (q *Queries) GetBetByCashoutID(ctx context.Context, cashoutID string) (BetWithOutcome, error) {
row := q.db.QueryRow(ctx, GetBetByCashoutID, cashoutID)
var i BetWithOutcome
err := row.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.Outcomes,
)
return i, err
}
const GetBetByFastCode = `-- name: GetBetByFastCode :one
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, outcomes
SELECT id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, created_at, updated_at, full_name, phone_number, outcomes
FROM bet_with_outcomes
WHERE fast_code = $1
LIMIT 1
@ -298,25 +194,22 @@ func (q *Queries) GetBetByFastCode(ctx context.Context, fastCode string) (BetWit
&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.CashedOut,
&i.OutcomesHash,
&i.FastCode,
&i.CreatedAt,
&i.UpdatedAt,
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
)
return i, err
}
const GetBetByID = `-- name: GetBetByID :one
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, outcomes
SELECT id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, created_at, updated_at, full_name, phone_number, outcomes
FROM bet_with_outcomes
WHERE id = $1
`
@ -329,30 +222,27 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err
&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.CashedOut,
&i.OutcomesHash,
&i.FastCode,
&i.CreatedAt,
&i.UpdatedAt,
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
)
return i, err
}
const GetBetByUserID = `-- name: GetBetByUserID :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, outcomes
SELECT id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, created_at, updated_at, full_name, phone_number, outcomes
FROM bet_with_outcomes
WHERE user_id = $1
`
func (q *Queries) GetBetByUserID(ctx context.Context, userID pgtype.Int8) ([]BetWithOutcome, error) {
func (q *Queries) GetBetByUserID(ctx context.Context, userID int64) ([]BetWithOutcome, error) {
rows, err := q.db.Query(ctx, GetBetByUserID, userID)
if err != nil {
return nil, err
@ -366,18 +256,15 @@ func (q *Queries) GetBetByUserID(ctx context.Context, userID pgtype.Int8) ([]Bet
&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.CashedOut,
&i.OutcomesHash,
&i.FastCode,
&i.CreatedAt,
&i.UpdatedAt,
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
); err != nil {
return nil, err
@ -393,13 +280,13 @@ func (q *Queries) GetBetByUserID(ctx context.Context, userID pgtype.Int8) ([]Bet
const GetBetCount = `-- name: GetBetCount :one
SELECT COUNT(*)
FROM bets
where user_id = $1
WHERE user_id = $1
AND outcomes_hash = $2
`
type GetBetCountParams struct {
UserID pgtype.Int8 `json:"user_id"`
OutcomesHash string `json:"outcomes_hash"`
UserID int64 `json:"user_id"`
OutcomesHash string `json:"outcomes_hash"`
}
func (q *Queries) GetBetCount(ctx context.Context, arg GetBetCountParams) (int64, error) {

View File

@ -22,23 +22,7 @@ func (q *Queries) DeleteEvent(ctx context.Context, id string) error {
}
const GetAllUpcomingEvents = `-- name: GetAllUpcomingEvents :many
SELECT id,
sport_id,
match_name,
home_team,
away_team,
home_team_id,
away_team_id,
home_kit_image,
away_kit_image,
league_id,
league_name,
league_cc,
start_time,
is_live,
status,
source,
fetched_at
SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, league_cc, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, flagged
FROM events
WHERE start_time > now()
AND is_live = false
@ -46,35 +30,15 @@ WHERE start_time > now()
ORDER BY start_time ASC
`
type GetAllUpcomingEventsRow struct {
ID string `json:"id"`
SportID pgtype.Int4 `json:"sport_id"`
MatchName pgtype.Text `json:"match_name"`
HomeTeam pgtype.Text `json:"home_team"`
AwayTeam pgtype.Text `json:"away_team"`
HomeTeamID pgtype.Int4 `json:"home_team_id"`
AwayTeamID pgtype.Int4 `json:"away_team_id"`
HomeKitImage pgtype.Text `json:"home_kit_image"`
AwayKitImage pgtype.Text `json:"away_kit_image"`
LeagueID pgtype.Int4 `json:"league_id"`
LeagueName pgtype.Text `json:"league_name"`
LeagueCc pgtype.Text `json:"league_cc"`
StartTime pgtype.Timestamp `json:"start_time"`
IsLive pgtype.Bool `json:"is_live"`
Status pgtype.Text `json:"status"`
Source pgtype.Text `json:"source"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
}
func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]GetAllUpcomingEventsRow, error) {
func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]Event, error) {
rows, err := q.db.Query(ctx, GetAllUpcomingEvents)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetAllUpcomingEventsRow
var items []Event
for rows.Next() {
var i GetAllUpcomingEventsRow
var i Event
if err := rows.Scan(
&i.ID,
&i.SportID,
@ -89,10 +53,16 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]GetAllUpcomingEve
&i.LeagueName,
&i.LeagueCc,
&i.StartTime,
&i.Score,
&i.MatchMinute,
&i.TimerStatus,
&i.AddedTime,
&i.MatchPeriod,
&i.IsLive,
&i.Status,
&i.Source,
&i.FetchedAt,
&i.Source,
&i.Flagged,
); err != nil {
return nil, err
}
@ -105,22 +75,7 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]GetAllUpcomingEve
}
const GetExpiredUpcomingEvents = `-- name: GetExpiredUpcomingEvents :many
SELECT events.id,
events.sport_id,
events.match_name,
events.home_team,
events.away_team,
events.home_team_id,
events.away_team_id,
events.home_kit_image,
events.away_kit_image,
events.league_id,
events.league_name,
events.start_time,
events.is_live,
events.status,
events.source,
events.fetched_at,
SELECT events.id, events.sport_id, events.match_name, events.home_team, events.away_team, events.home_team_id, events.away_team_id, events.home_kit_image, events.away_kit_image, events.league_id, events.league_name, events.league_cc, events.start_time, events.score, events.match_minute, events.timer_status, events.added_time, events.match_period, events.is_live, events.status, events.fetched_at, events.source, events.flagged,
leagues.country_code as league_cc
FROM events
LEFT JOIN leagues ON leagues.id = league_id
@ -144,12 +99,19 @@ type GetExpiredUpcomingEventsRow struct {
AwayKitImage pgtype.Text `json:"away_kit_image"`
LeagueID pgtype.Int4 `json:"league_id"`
LeagueName pgtype.Text `json:"league_name"`
LeagueCc pgtype.Text `json:"league_cc"`
StartTime pgtype.Timestamp `json:"start_time"`
Score pgtype.Text `json:"score"`
MatchMinute pgtype.Int4 `json:"match_minute"`
TimerStatus pgtype.Text `json:"timer_status"`
AddedTime pgtype.Int4 `json:"added_time"`
MatchPeriod pgtype.Int4 `json:"match_period"`
IsLive pgtype.Bool `json:"is_live"`
Status pgtype.Text `json:"status"`
Source pgtype.Text `json:"source"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
LeagueCc pgtype.Text `json:"league_cc"`
Source pgtype.Text `json:"source"`
Flagged bool `json:"flagged"`
LeagueCc_2 pgtype.Text `json:"league_cc_2"`
}
func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Text) ([]GetExpiredUpcomingEventsRow, error) {
@ -173,12 +135,19 @@ func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Te
&i.AwayKitImage,
&i.LeagueID,
&i.LeagueName,
&i.LeagueCc,
&i.StartTime,
&i.Score,
&i.MatchMinute,
&i.TimerStatus,
&i.AddedTime,
&i.MatchPeriod,
&i.IsLive,
&i.Status,
&i.Source,
&i.FetchedAt,
&i.LeagueCc,
&i.Source,
&i.Flagged,
&i.LeagueCc_2,
); err != nil {
return nil, err
}
@ -191,22 +160,7 @@ func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Te
}
const GetPaginatedUpcomingEvents = `-- name: GetPaginatedUpcomingEvents :many
SELECT events.id,
events.sport_id,
events.match_name,
events.home_team,
events.away_team,
events.home_team_id,
events.away_team_id,
events.home_kit_image,
events.away_kit_image,
events.league_id,
events.league_name,
events.start_time,
events.is_live,
events.status,
events.source,
events.fetched_at,
SELECT events.id, events.sport_id, events.match_name, events.home_team, events.away_team, events.home_team_id, events.away_team_id, events.home_kit_image, events.away_kit_image, events.league_id, events.league_name, events.league_cc, events.start_time, events.score, events.match_minute, events.timer_status, events.added_time, events.match_period, events.is_live, events.status, events.fetched_at, events.source, events.flagged,
leagues.country_code as league_cc
FROM events
LEFT JOIN leagues ON leagues.id = league_id
@ -259,12 +213,19 @@ type GetPaginatedUpcomingEventsRow struct {
AwayKitImage pgtype.Text `json:"away_kit_image"`
LeagueID pgtype.Int4 `json:"league_id"`
LeagueName pgtype.Text `json:"league_name"`
LeagueCc pgtype.Text `json:"league_cc"`
StartTime pgtype.Timestamp `json:"start_time"`
Score pgtype.Text `json:"score"`
MatchMinute pgtype.Int4 `json:"match_minute"`
TimerStatus pgtype.Text `json:"timer_status"`
AddedTime pgtype.Int4 `json:"added_time"`
MatchPeriod pgtype.Int4 `json:"match_period"`
IsLive pgtype.Bool `json:"is_live"`
Status pgtype.Text `json:"status"`
Source pgtype.Text `json:"source"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
LeagueCc pgtype.Text `json:"league_cc"`
Source pgtype.Text `json:"source"`
Flagged bool `json:"flagged"`
LeagueCc_2 pgtype.Text `json:"league_cc_2"`
}
func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginatedUpcomingEventsParams) ([]GetPaginatedUpcomingEventsRow, error) {
@ -296,12 +257,19 @@ func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginat
&i.AwayKitImage,
&i.LeagueID,
&i.LeagueName,
&i.LeagueCc,
&i.StartTime,
&i.Score,
&i.MatchMinute,
&i.TimerStatus,
&i.AddedTime,
&i.MatchPeriod,
&i.IsLive,
&i.Status,
&i.Source,
&i.FetchedAt,
&i.LeagueCc,
&i.Source,
&i.Flagged,
&i.LeagueCc_2,
); err != nil {
return nil, err
}
@ -363,23 +331,7 @@ func (q *Queries) GetTotalEvents(ctx context.Context, arg GetTotalEventsParams)
}
const GetUpcomingByID = `-- name: GetUpcomingByID :one
SELECT id,
sport_id,
match_name,
home_team,
away_team,
home_team_id,
away_team_id,
home_kit_image,
away_kit_image,
league_id,
league_name,
league_cc,
start_time,
is_live,
status,
source,
fetched_at
SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, league_cc, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, flagged
FROM events
WHERE id = $1
AND is_live = false
@ -387,29 +339,9 @@ WHERE id = $1
LIMIT 1
`
type GetUpcomingByIDRow struct {
ID string `json:"id"`
SportID pgtype.Int4 `json:"sport_id"`
MatchName pgtype.Text `json:"match_name"`
HomeTeam pgtype.Text `json:"home_team"`
AwayTeam pgtype.Text `json:"away_team"`
HomeTeamID pgtype.Int4 `json:"home_team_id"`
AwayTeamID pgtype.Int4 `json:"away_team_id"`
HomeKitImage pgtype.Text `json:"home_kit_image"`
AwayKitImage pgtype.Text `json:"away_kit_image"`
LeagueID pgtype.Int4 `json:"league_id"`
LeagueName pgtype.Text `json:"league_name"`
LeagueCc pgtype.Text `json:"league_cc"`
StartTime pgtype.Timestamp `json:"start_time"`
IsLive pgtype.Bool `json:"is_live"`
Status pgtype.Text `json:"status"`
Source pgtype.Text `json:"source"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
}
func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (GetUpcomingByIDRow, error) {
func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (Event, error) {
row := q.db.QueryRow(ctx, GetUpcomingByID, id)
var i GetUpcomingByIDRow
var i Event
err := row.Scan(
&i.ID,
&i.SportID,
@ -424,10 +356,16 @@ func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (GetUpcomingBy
&i.LeagueName,
&i.LeagueCc,
&i.StartTime,
&i.Score,
&i.MatchMinute,
&i.TimerStatus,
&i.AddedTime,
&i.MatchPeriod,
&i.IsLive,
&i.Status,
&i.Source,
&i.FetchedAt,
&i.Source,
&i.Flagged,
)
return i, err
}
@ -673,11 +611,26 @@ func (q *Queries) ListLiveEvents(ctx context.Context) ([]string, error) {
return items, nil
}
const UpdateFlagged = `-- name: UpdateFlagged :exec
UPDATE events
SET flagged = $1
WHERE id = $2
`
type UpdateFlaggedParams struct {
Flagged bool `json:"flagged"`
ID string `json:"id"`
}
func (q *Queries) UpdateFlagged(ctx context.Context, arg UpdateFlaggedParams) error {
_, err := q.db.Exec(ctx, UpdateFlagged, arg.Flagged, arg.ID)
return err
}
const UpdateMatchResult = `-- name: UpdateMatchResult :exec
UPDATE events
SET score = $1,
status = $2,
fetched_at = NOW()
status = $2
WHERE id = $3
`

View File

@ -10,7 +10,8 @@ import (
)
const CountReportedIssues = `-- name: CountReportedIssues :one
SELECT COUNT(*) FROM reported_issues
SELECT COUNT(*)
FROM reported_issues
`
func (q *Queries) CountReportedIssues(ctx context.Context) (int64, error) {
@ -20,12 +21,14 @@ func (q *Queries) CountReportedIssues(ctx context.Context) (int64, error) {
return count, err
}
const CountReportedIssuesByCustomer = `-- name: CountReportedIssuesByCustomer :one
SELECT COUNT(*) FROM reported_issues WHERE customer_id = $1
const CountReportedIssuesByUser = `-- name: CountReportedIssuesByUser :one
SELECT COUNT(*)
FROM reported_issues
WHERE user_id = $1
`
func (q *Queries) CountReportedIssuesByCustomer(ctx context.Context, customerID int64) (int64, error) {
row := q.db.QueryRow(ctx, CountReportedIssuesByCustomer, customerID)
func (q *Queries) CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error) {
row := q.db.QueryRow(ctx, CountReportedIssuesByUser, userID)
var count int64
err := row.Scan(&count)
return count, err
@ -33,15 +36,20 @@ func (q *Queries) CountReportedIssuesByCustomer(ctx context.Context, customerID
const CreateReportedIssue = `-- name: CreateReportedIssue :one
INSERT INTO reported_issues (
customer_id, subject, description, issue_type, metadata
) VALUES (
$1, $2, $3, $4, $5
)
RETURNING id, customer_id, subject, description, issue_type, status, metadata, created_at, updated_at
user_id,
user_role,
subject,
description,
issue_type,
metadata
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at
`
type CreateReportedIssueParams struct {
CustomerID int64 `json:"customer_id"`
UserID int64 `json:"user_id"`
UserRole string `json:"user_role"`
Subject string `json:"subject"`
Description string `json:"description"`
IssueType string `json:"issue_type"`
@ -50,7 +58,8 @@ type CreateReportedIssueParams struct {
func (q *Queries) CreateReportedIssue(ctx context.Context, arg CreateReportedIssueParams) (ReportedIssue, error) {
row := q.db.QueryRow(ctx, CreateReportedIssue,
arg.CustomerID,
arg.UserID,
arg.UserRole,
arg.Subject,
arg.Description,
arg.IssueType,
@ -59,7 +68,8 @@ func (q *Queries) CreateReportedIssue(ctx context.Context, arg CreateReportedIss
var i ReportedIssue
err := row.Scan(
&i.ID,
&i.CustomerID,
&i.UserID,
&i.UserRole,
&i.Subject,
&i.Description,
&i.IssueType,
@ -72,7 +82,8 @@ func (q *Queries) CreateReportedIssue(ctx context.Context, arg CreateReportedIss
}
const DeleteReportedIssue = `-- name: DeleteReportedIssue :exec
DELETE FROM reported_issues WHERE id = $1
DELETE FROM reported_issues
WHERE id = $1
`
func (q *Queries) DeleteReportedIssue(ctx context.Context, id int64) error {
@ -81,7 +92,8 @@ func (q *Queries) DeleteReportedIssue(ctx context.Context, id int64) error {
}
const ListReportedIssues = `-- name: ListReportedIssues :many
SELECT id, customer_id, subject, description, issue_type, status, metadata, created_at, updated_at FROM reported_issues
SELECT id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at
FROM reported_issues
ORDER BY created_at DESC
LIMIT $1 OFFSET $2
`
@ -102,7 +114,8 @@ func (q *Queries) ListReportedIssues(ctx context.Context, arg ListReportedIssues
var i ReportedIssue
if err := rows.Scan(
&i.ID,
&i.CustomerID,
&i.UserID,
&i.UserRole,
&i.Subject,
&i.Description,
&i.IssueType,
@ -121,21 +134,22 @@ func (q *Queries) ListReportedIssues(ctx context.Context, arg ListReportedIssues
return items, nil
}
const ListReportedIssuesByCustomer = `-- name: ListReportedIssuesByCustomer :many
SELECT id, customer_id, subject, description, issue_type, status, metadata, created_at, updated_at FROM reported_issues
WHERE customer_id = $1
const ListReportedIssuesByUser = `-- name: ListReportedIssuesByUser :many
SELECT id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at
FROM reported_issues
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3
`
type ListReportedIssuesByCustomerParams struct {
CustomerID int64 `json:"customer_id"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
type ListReportedIssuesByUserParams struct {
UserID int64 `json:"user_id"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
func (q *Queries) ListReportedIssuesByCustomer(ctx context.Context, arg ListReportedIssuesByCustomerParams) ([]ReportedIssue, error) {
rows, err := q.db.Query(ctx, ListReportedIssuesByCustomer, arg.CustomerID, arg.Limit, arg.Offset)
func (q *Queries) ListReportedIssuesByUser(ctx context.Context, arg ListReportedIssuesByUserParams) ([]ReportedIssue, error) {
rows, err := q.db.Query(ctx, ListReportedIssuesByUser, arg.UserID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
@ -145,7 +159,8 @@ func (q *Queries) ListReportedIssuesByCustomer(ctx context.Context, arg ListRepo
var i ReportedIssue
if err := rows.Scan(
&i.ID,
&i.CustomerID,
&i.UserID,
&i.UserRole,
&i.Subject,
&i.Description,
&i.IssueType,
@ -166,7 +181,8 @@ func (q *Queries) ListReportedIssuesByCustomer(ctx context.Context, arg ListRepo
const UpdateReportedIssueStatus = `-- name: UpdateReportedIssueStatus :exec
UPDATE reported_issues
SET status = $2, updated_at = NOW()
SET status = $2,
updated_at = NOW()
WHERE id = $1
`

View File

@ -78,18 +78,13 @@ type Bet struct {
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
Status int32 `json:"status"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
CompanyID pgtype.Int8 `json:"company_id"`
BranchID pgtype.Int8 `json:"branch_id"`
UserID pgtype.Int8 `json:"user_id"`
CashedOut bool `json:"cashed_out"`
CashoutID string `json:"cashout_id"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
UserID int64 `json:"user_id"`
IsShopBet bool `json:"is_shop_bet"`
CashedOut bool `json:"cashed_out"`
OutcomesHash string `json:"outcomes_hash"`
FastCode string `json:"fast_code"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type BetOutcome struct {
@ -115,18 +110,15 @@ type BetWithOutcome struct {
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
Status int32 `json:"status"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
CompanyID pgtype.Int8 `json:"company_id"`
BranchID pgtype.Int8 `json:"branch_id"`
UserID pgtype.Int8 `json:"user_id"`
CashedOut bool `json:"cashed_out"`
CashoutID string `json:"cashout_id"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
UserID int64 `json:"user_id"`
IsShopBet bool `json:"is_shop_bet"`
CashedOut bool `json:"cashed_out"`
OutcomesHash string `json:"outcomes_hash"`
FastCode string `json:"fast_code"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
FullName interface{} `json:"full_name"`
PhoneNumber pgtype.Text `json:"phone_number"`
Outcomes []BetOutcome `json:"outcomes"`
}
@ -252,6 +244,7 @@ type Event struct {
Status pgtype.Text `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
Source pgtype.Text `json:"source"`
Flagged bool `json:"flagged"`
}
type ExchangeRate struct {
@ -366,7 +359,8 @@ type RefreshToken struct {
type ReportedIssue struct {
ID int64 `json:"id"`
CustomerID int64 `json:"customer_id"`
UserID int64 `json:"user_id"`
UserRole string `json:"user_role"`
Subject string `json:"subject"`
Description string `json:"description"`
IssueType string `json:"issue_type"`

View File

@ -12,18 +12,35 @@ import (
)
const GetBranchWiseReport = `-- name: GetBranchWiseReport :many
SELECT
b.branch_id,
br.name AS branch_name,
br.company_id,
COUNT(*) AS total_bets,
COALESCE(SUM(b.amount), 0) AS total_cash_made,
COALESCE(SUM(CASE WHEN b.cashed_out THEN b.amount ELSE 0 END), 0) AS total_cash_out,
COALESCE(SUM(CASE WHEN b.status = 5 THEN b.amount ELSE 0 END), 0) AS total_cash_backs
FROM bets b
JOIN branches br ON b.branch_id = br.id
SELECT b.branch_id,
br.name AS branch_name,
br.company_id,
COUNT(*) AS total_bets,
COALESCE(SUM(b.amount), 0) AS total_cash_made,
COALESCE(
SUM(
CASE
WHEN b.cashed_out THEN b.amount
ELSE 0
END
),
0
) AS total_cash_out,
COALESCE(
SUM(
CASE
WHEN b.status = 5 THEN b.amount
ELSE 0
END
),
0
) AS total_cash_backs
FROM shop_bet_detail b
JOIN branches br ON b.branch_id = br.id
WHERE b.created_at BETWEEN $1 AND $2
GROUP BY b.branch_id, br.name, br.company_id
GROUP BY b.branch_id,
br.name,
br.company_id
`
type GetBranchWiseReportParams struct {
@ -32,7 +49,7 @@ type GetBranchWiseReportParams struct {
}
type GetBranchWiseReportRow struct {
BranchID pgtype.Int8 `json:"branch_id"`
BranchID int64 `json:"branch_id"`
BranchName string `json:"branch_name"`
CompanyID int64 `json:"company_id"`
TotalBets int64 `json:"total_bets"`
@ -70,17 +87,33 @@ func (q *Queries) GetBranchWiseReport(ctx context.Context, arg GetBranchWiseRepo
}
const GetCompanyWiseReport = `-- name: GetCompanyWiseReport :many
SELECT
b.company_id,
c.name AS company_name,
COUNT(*) AS total_bets,
COALESCE(SUM(b.amount), 0) AS total_cash_made,
COALESCE(SUM(CASE WHEN b.cashed_out THEN b.amount ELSE 0 END), 0) AS total_cash_out,
COALESCE(SUM(CASE WHEN b.status = 5 THEN b.amount ELSE 0 END), 0) AS total_cash_backs
FROM bets b
JOIN companies c ON b.company_id = c.id
SELECT b.company_id,
c.name AS company_name,
COUNT(*) AS total_bets,
COALESCE(SUM(b.amount), 0) AS total_cash_made,
COALESCE(
SUM(
CASE
WHEN b.cashed_out THEN b.amount
ELSE 0
END
),
0
) AS total_cash_out,
COALESCE(
SUM(
CASE
WHEN b.status = 5 THEN b.amount
ELSE 0
END
),
0
) AS total_cash_backs
FROM shop_bet_detail b
JOIN companies c ON b.company_id = c.id
WHERE b.created_at BETWEEN $1 AND $2
GROUP BY b.company_id, c.name
GROUP BY b.company_id,
c.name
`
type GetCompanyWiseReportParams struct {
@ -89,7 +122,7 @@ type GetCompanyWiseReportParams struct {
}
type GetCompanyWiseReportRow struct {
CompanyID pgtype.Int8 `json:"company_id"`
CompanyID int64 `json:"company_id"`
CompanyName string `json:"company_name"`
TotalBets int64 `json:"total_bets"`
TotalCashMade interface{} `json:"total_cash_made"`

View File

@ -38,29 +38,23 @@ type CreateBetOutcome struct {
Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
}
// If it is a ShopBet then UserID will be the cashier
// If it is a DigitalBet then UserID will be the user and the branchID will be 0 or nil
// If it is a ShopBet then UserID and Fullname will be the cashier
// If it is a DigitalBet then UserID and Fullname will be the user
type Bet struct {
ID int64
Amount Currency
TotalOdds float32
Status OutcomeStatus
FullName string
PhoneNumber string
BranchID ValidInt64 // Can Be Nullable
CompanyID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable
IsShopBet bool
CashedOut bool
CashoutID string
FastCode string
CreatedAt time.Time
ID int64
Amount Currency
TotalOdds float32
Status OutcomeStatus
UserID int64
IsShopBet bool
CashedOut bool
FastCode string
CreatedAt time.Time
}
type BetFilter struct {
BranchID ValidInt64 // Can Be Nullable
CompanyID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable
UserID ValidInt64
CashedOut ValidBool
IsShopBet ValidBool
Query ValidString
CreatedBefore ValidTime
@ -74,12 +68,9 @@ type GetBet struct {
Status OutcomeStatus
FullName string
PhoneNumber string
BranchID ValidInt64 // Can Be Nullable
CompanyID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable
UserID int64
IsShopBet bool
CashedOut bool
CashoutID string
Outcomes []BetOutcome
FastCode string
CreatedAt time.Time
@ -89,13 +80,8 @@ type CreateBet struct {
Amount Currency
TotalOdds float32
Status OutcomeStatus
FullName string
PhoneNumber string
CompanyID ValidInt64 // Can Be Nullable
BranchID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable
UserID int64
IsShopBet bool
CashoutID string
OutcomesHash string
FastCode string
}
@ -107,11 +93,8 @@ type CreateBetOutcomeReq struct {
}
type CreateBetReq struct {
Outcomes []CreateBetOutcomeReq `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
FullName string `json:"full_name" example:"John"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
BranchID *int64 `json:"branch_id,omitempty" example:"1"`
Outcomes []CreateBetOutcomeReq `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
}
type RandomBetReq struct {
@ -124,28 +107,22 @@ type CreateBetRes struct {
Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"`
Status OutcomeStatus `json:"status" example:"1"`
FullName string `json:"full_name" example:"John"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
BranchID int64 `json:"branch_id" example:"2"`
UserID int64 `json:"user_id" example:"2"`
IsShopBet bool `json:"is_shop_bet" example:"false"`
CreatedNumber int64 `json:"created_number" example:"2"`
CashedID string `json:"cashed_id" example:"21234"`
}
type BetRes struct {
ID int64 `json:"id" example:"1"`
Outcomes []BetOutcome `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"`
Status OutcomeStatus `json:"status" example:"1"`
FullName string `json:"full_name" example:"John"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
BranchID int64 `json:"branch_id" example:"2"`
UserID int64 `json:"user_id" example:"2"`
IsShopBet bool `json:"is_shop_bet" example:"false"`
CashedOut bool `json:"cashed_out" example:"false"`
CashedID string `json:"cashed_id" example:"21234"`
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
ID int64 `json:"id" example:"1"`
Outcomes []BetOutcome `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"`
Status OutcomeStatus `json:"status" example:"1"`
Fullname string `json:"full_name" example:"John Smith"`
UserID int64 `json:"user_id" example:"2"`
IsShopBet bool `json:"is_shop_bet" example:"false"`
CashedOut bool `json:"cashed_out" example:"false"`
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
FastCode string `json:"fast_code"`
}
func ConvertCreateBet(bet Bet, createdNumber int64) CreateBetRes {
@ -154,29 +131,23 @@ func ConvertCreateBet(bet Bet, createdNumber int64) CreateBetRes {
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: bet.BranchID.Value,
UserID: bet.UserID.Value,
UserID: bet.UserID,
CreatedNumber: createdNumber,
CashedID: bet.CashoutID,
}
}
func ConvertBet(bet GetBet) BetRes {
return BetRes{
ID: bet.ID,
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: bet.BranchID.Value,
UserID: bet.UserID.Value,
Outcomes: bet.Outcomes,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CashedID: bet.CashoutID,
CreatedAt: bet.CreatedAt,
ID: bet.ID,
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
Fullname: bet.FullName,
UserID: bet.UserID,
Outcomes: bet.Outcomes,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CreatedAt: bet.CreatedAt,
FastCode: bet.FastCode,
}
}

View File

@ -101,6 +101,7 @@ type UpcomingEvent struct {
StartTime time.Time `json:"start_time"` // Converted from "time" field in UNIX format
Source string `json:"source"` // bet api provider (bet365, betfair)
Status EventStatus `json:"status"` //Match Status for event
Flagged bool `json:"flagged"` //Whether the event is flagged or not
}
type MatchResult struct {
EventID string

View File

@ -2,14 +2,51 @@ package domain
import "time"
type ReportedIssueType string
var (
ISSUE_TYPE_DEPOSIT ReportedIssueType = "deposit"
ISSUE_TYPE_WITHDRAWAL ReportedIssueType = "withdrawal"
ISSUE_TYPE_BET ReportedIssueType = "bet"
ISSUE_TYPE_CASHOUT ReportedIssueType = "cashout"
ISSUE_TYPE_ODDS ReportedIssueType = "odds"
ISSUE_TYPE_EVENTS ReportedIssueType = "events"
ISSUE_TYPE_BRANCH ReportedIssueType = "branch"
ISSUE_TYPE_USER ReportedIssueType = "branch"
ISSUE_TYPE_LOGIN ReportedIssueType = "login"
ISSUE_TYPE_REGISTER ReportedIssueType = "register"
ISSUE_TYPE_RESET_PASSWORD ReportedIssueType = "reset_password"
ISSUE_TYPE_WALLET ReportedIssueType = "wallet"
ISSUE_TYPE_VIRTUAL ReportedIssueType = "virtual games"
ISSUE_TYPE_OTHER ReportedIssueType = "other"
)
type ReportedIssueStatus string
var (
ISSUE_STATUS_PENDING ReportedIssueStatus = "pending"
ISSUE_STATUS_IN_PROGRESS ReportedIssueStatus = "in_progress"
ISSUE_STATUS_RESOLVED ReportedIssueStatus = "resolved"
ISSUE_STATUS_REJECTED ReportedIssueStatus = "rejected"
)
type ReportedIssue struct {
ID int64 `json:"id"`
CustomerID int64 `json:"customer_id"`
UserID int64 `json:"user_id"`
UserRole Role `json:"user_role"`
Subject string `json:"subject"`
Description string `json:"description"`
IssueType string `json:"issue_type"`
Status string `json:"status"`
IssueType ReportedIssueType `json:"issue_type"`
Status ReportedIssueStatus `json:"status"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ReportedIssueReq struct {
ID int64 `json:"id"`
Subject string `json:"subject"`
Description string `json:"description"`
IssueType ReportedIssueType `json:"issue_type"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}

View File

@ -21,27 +21,14 @@ var (
func convertDBBet(bet dbgen.Bet) domain.Bet {
return domain.Bet{
ID: bet.ID,
Amount: domain.Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: domain.OutcomeStatus(bet.Status),
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: domain.ValidInt64{
Value: bet.BranchID.Int64,
Valid: bet.BranchID.Valid,
},
CompanyID: domain.ValidInt64{
Value: bet.CompanyID.Int64,
Valid: bet.CompanyID.Valid,
},
UserID: domain.ValidInt64{
Value: bet.UserID.Int64,
Valid: bet.UserID.Valid,
},
ID: bet.ID,
Amount: domain.Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: domain.OutcomeStatus(bet.Status),
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CashoutID: bet.CashoutID,
FastCode: bet.FastCode,
CreatedAt: bet.CreatedAt.Time,
}
}
@ -78,21 +65,14 @@ func convertDBBetWithOutcomes(bet dbgen.BetWithOutcome) domain.GetBet {
Amount: domain.Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: domain.OutcomeStatus(bet.Status),
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: domain.ValidInt64{
Value: bet.BranchID.Int64,
Valid: bet.BranchID.Valid,
},
UserID: domain.ValidInt64{
Value: bet.UserID.Int64,
Valid: bet.UserID.Valid,
},
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CashoutID: bet.CashoutID,
Outcomes: outcomes,
CreatedAt: bet.CreatedAt.Time,
FullName: bet.FullName.(string),
PhoneNumber: bet.PhoneNumber.String,
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
Outcomes: outcomes,
FastCode: bet.FastCode,
CreatedAt: bet.CreatedAt.Time,
}
}
@ -119,22 +99,11 @@ func convertDBCreateBetOutcome(betOutcome domain.CreateBetOutcome) dbgen.CreateB
func convertCreateBet(bet domain.CreateBet) dbgen.CreateBetParams {
return dbgen.CreateBetParams{
Amount: int64(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: int32(bet.Status),
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: pgtype.Int8{
Int64: bet.BranchID.Value,
Valid: bet.BranchID.Valid,
},
UserID: pgtype.Int8{
Int64: bet.UserID.Value,
Valid: bet.UserID.Valid,
},
Amount: int64(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: int32(bet.Status),
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
CashoutID: bet.CashoutID,
OutcomesHash: bet.OutcomesHash,
FastCode: bet.FastCode,
}
@ -184,33 +153,16 @@ func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error)
return convertDBBetWithOutcomes(bet), nil
}
func (s *Store) GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error) {
bet, err := s.queries.GetBetByCashoutID(ctx, id)
if err != nil {
domain.MongoDBLogger.Error("failed to get bet by cashout ID",
zap.String("cashout_id", id),
zap.Error(err),
)
return domain.GetBet{}, err
}
return convertDBBetWithOutcomes(bet), nil
}
func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, error) {
bets, err := s.queries.GetAllBets(ctx, dbgen.GetAllBetsParams{
BranchID: pgtype.Int8{
Int64: filter.BranchID.Value,
Valid: filter.BranchID.Valid,
},
CompanyID: pgtype.Int8{
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.Valid,
},
UserID: pgtype.Int8{
Int64: filter.UserID.Value,
Valid: filter.UserID.Valid,
},
CashedOut: pgtype.Bool{
Bool: filter.CashedOut.Value,
Valid: filter.CashedOut.Valid,
},
IsShopBet: pgtype.Bool{
Bool: filter.IsShopBet.Value,
Valid: filter.IsShopBet.Valid,
@ -244,32 +196,8 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma
return result, nil
}
func (s *Store) GetBetByBranchID(ctx context.Context, BranchID int64) ([]domain.GetBet, error) {
bets, err := s.queries.GetBetByBranchID(ctx, pgtype.Int8{
Int64: BranchID,
Valid: true,
})
if err != nil {
domain.MongoDBLogger.Error("failed to get bets by branch ID",
zap.Int64("branch_id", BranchID),
zap.Error(err),
)
return nil, err
}
var result []domain.GetBet = make([]domain.GetBet, 0, len(bets))
for _, bet := range bets {
result = append(result, convertDBBetWithOutcomes(bet))
}
return result, nil
}
func (s *Store) GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) {
bets, err := s.queries.GetBetByUserID(ctx, pgtype.Int8{
Int64: UserID,
Valid: true,
})
bets, err := s.queries.GetBetByUserID(ctx, UserID)
if err != nil {
return nil, err
@ -295,7 +223,7 @@ func (s *Store) GetBetByFastCode(ctx context.Context, fastcode string) (domain.G
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},
UserID: UserID,
OutcomesHash: outcomesHash,
})
@ -462,21 +390,21 @@ func (s *Store) GetBetSummary(ctx context.Context, filter domain.ReportFilter) (
argPos := 1
// Add filters if provided
if filter.CompanyID.Valid {
query += fmt.Sprintf(" WHERE company_id = $%d", argPos)
args = append(args, filter.CompanyID.Value)
argPos++
}
if filter.BranchID.Valid {
query += fmt.Sprintf(" AND %sbranch_id = $%d", func() string {
if len(args) == 0 {
return " WHERE "
}
return " AND "
}(), argPos)
args = append(args, filter.BranchID.Value)
argPos++
}
// if filter.CompanyID.Valid {
// query += fmt.Sprintf(" WHERE company_id = $%d", argPos)
// args = append(args, filter.CompanyID.Value)
// argPos++
// }
// if filter.BranchID.Valid {
// query += fmt.Sprintf(" AND %sbranch_id = $%d", func() string {
// if len(args) == 0 {
// return " WHERE "
// }
// return " AND "
// }(), argPos)
// args = append(args, filter.BranchID.Value)
// argPos++
// }
if filter.UserID.Valid {
query += fmt.Sprintf(" AND %suser_id = $%d", func() string {
if len(args) == 0 {

View File

@ -180,6 +180,7 @@ func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.Ev
StartTime: e.StartTime.Time.UTC(),
Source: e.Source.String,
Status: domain.EventStatus(e.Status.String),
}
}
totalCount, err := s.queries.GetTotalEvents(ctx, dbgen.GetTotalEventsParams{
@ -268,6 +269,14 @@ func (s *Store) UpdateEventStatus(ctx context.Context, eventID string, status do
}
func (s *Store) UpdateFlagged(ctx context.Context, eventID string, flagged bool) error {
return s.queries.UpdateFlagged(ctx, dbgen.UpdateFlaggedParams{
ID: eventID,
Flagged: flagged,
})
}
func (s *Store) DeleteEvent(ctx context.Context, eventID string) error {
err := s.queries.DeleteEvent(ctx, eventID)
if err != nil {

View File

@ -9,9 +9,9 @@ import (
type ReportedIssueRepository interface {
CreateReportedIssue(ctx context.Context, arg dbgen.CreateReportedIssueParams) (dbgen.ReportedIssue, error)
ListReportedIssues(ctx context.Context, limit, offset int32) ([]dbgen.ReportedIssue, error)
ListReportedIssuesByCustomer(ctx context.Context, customerID int64, limit, offset int32) ([]dbgen.ReportedIssue, error)
ListReportedIssuesByUser(ctx context.Context, userID int64, limit, offset int32) ([]dbgen.ReportedIssue, error)
CountReportedIssues(ctx context.Context) (int64, error)
CountReportedIssuesByCustomer(ctx context.Context, customerID int64) (int64, error)
CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error)
UpdateReportedIssueStatus(ctx context.Context, id int64, status string) error
DeleteReportedIssue(ctx context.Context, id int64) error
}
@ -36,21 +36,21 @@ func (s *ReportedIssueRepo) ListReportedIssues(ctx context.Context, limit, offse
return s.store.queries.ListReportedIssues(ctx, params)
}
func (s *ReportedIssueRepo) ListReportedIssuesByCustomer(ctx context.Context, customerID int64, limit, offset int32) ([]dbgen.ReportedIssue, error) {
params := dbgen.ListReportedIssuesByCustomerParams{
CustomerID: customerID,
Limit: limit,
Offset: offset,
func (s *ReportedIssueRepo) ListReportedIssuesByUser(ctx context.Context, userID int64, limit, offset int32) ([]dbgen.ReportedIssue, error) {
params := dbgen.ListReportedIssuesByUserParams{
UserID: userID,
Limit: limit,
Offset: offset,
}
return s.store.queries.ListReportedIssuesByCustomer(ctx, params)
return s.store.queries.ListReportedIssuesByUser(ctx, params)
}
func (s *ReportedIssueRepo) CountReportedIssues(ctx context.Context) (int64, error) {
return s.store.queries.CountReportedIssues(ctx)
}
func (s *ReportedIssueRepo) CountReportedIssuesByCustomer(ctx context.Context, customerID int64) (int64, error) {
return s.store.queries.CountReportedIssuesByCustomer(ctx, customerID)
func (s *ReportedIssueRepo) CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error) {
return s.store.queries.CountReportedIssuesByUser(ctx, userID)
}
func (s *ReportedIssueRepo) UpdateReportedIssueStatus(ctx context.Context, id int64, status string) error {

View File

@ -20,7 +20,9 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"go.uber.org/zap"
)
@ -36,16 +38,21 @@ var (
ErrBranchIDRequired = errors.New("Branch ID required for this role")
ErrOutcomeLimit = errors.New("Too many outcomes on a single bet")
ErrTotalBalanceNotEnough = errors.New("Total Wallet balance is insufficient to create bet")
ErrInvalidAmount = errors.New("Invalid amount")
ErrBetAmountTooHigh = errors.New("Cannot create a bet with an amount above limit")
)
type Service struct {
betStore BetStore
eventSvc event.Service
prematchSvc odds.ServiceImpl
walletSvc wallet.Service
branchSvc branch.Service
logger *slog.Logger
mongoLogger *zap.Logger
betStore BetStore
eventSvc event.Service
prematchSvc odds.ServiceImpl
walletSvc wallet.Service
branchSvc branch.Service
settingSvc settings.Service
notificationSvc *notificationservice.Service
logger *slog.Logger
mongoLogger *zap.Logger
}
func NewService(
@ -54,17 +61,21 @@ func NewService(
prematchSvc odds.ServiceImpl,
walletSvc wallet.Service,
branchSvc branch.Service,
settingSvc settings.Service,
notificationSvc *notificationservice.Service,
logger *slog.Logger,
mongoLogger *zap.Logger,
) *Service {
return &Service{
betStore: betStore,
eventSvc: eventSvc,
prematchSvc: prematchSvc,
walletSvc: walletSvc,
branchSvc: branchSvc,
logger: logger,
mongoLogger: mongoLogger,
betStore: betStore,
eventSvc: eventSvc,
prematchSvc: prematchSvc,
walletSvc: walletSvc,
branchSvc: branchSvc,
settingSvc: settingSvc,
notificationSvc: notificationSvc,
logger: logger,
mongoLogger: mongoLogger,
}
}
@ -196,7 +207,17 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
}
func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID int64, role domain.Role) (domain.CreateBetRes, error) {
if len(req.Outcomes) > 30 {
settingsList, err := s.settingSvc.GetSettingList(ctx)
if req.Amount < 1 {
return domain.CreateBetRes{}, ErrInvalidAmount
}
if req.Amount > settingsList.BetAmountLimit.Float32() {
return domain.CreateBetRes{}, ErrInvalidAmount
}
if len(req.Outcomes) > int(settingsList.MaxNumberOfOutcomes) {
s.mongoLogger.Error("too many outcomes",
zap.Int("count", len(req.Outcomes)),
zap.Int64("user_id", userID),
@ -237,7 +258,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
return domain.CreateBetRes{}, err
}
if count >= 2 {
return domain.CreateBetRes{}, fmt.Errorf("bet already pleaced twice")
return domain.CreateBetRes{}, fmt.Errorf("bet already placed twice")
}
cashoutID, err := s.GenerateCashoutID()

View File

@ -16,4 +16,5 @@ type Service interface {
// GetAndStoreMatchResult(ctx context.Context, eventID string) error
UpdateFinalScore(ctx context.Context, eventID, fullScore string, status domain.EventStatus) error
UpdateEventStatus(ctx context.Context, eventID string, status domain.EventStatus) error
UpdateFlagged(ctx context.Context, eventID string, flagged bool) error
}

View File

@ -369,6 +369,10 @@ func (s *service) UpdateEventStatus(ctx context.Context, eventID string, status
return s.store.UpdateEventStatus(ctx, eventID, status)
}
func (s *service) UpdateFlagged(ctx context.Context, eventID string, flagged bool) error {
return s.store.UpdateFlagged(ctx, eventID, flagged)
}
// func (s *service) GetAndStoreMatchResult(ctx context.Context, eventID string) error {
// url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%s", s.token, eventID)

View File

@ -17,15 +17,19 @@ func New(repo repository.ReportedIssueRepository) *Service {
return &Service{repo: repo}
}
func (s *Service) CreateReportedIssue(ctx context.Context, issue domain.ReportedIssue) (domain.ReportedIssue, error) {
func (s *Service) CreateReportedIssue(ctx context.Context, issue domain.ReportedIssueReq, userID int64, role domain.Role) (domain.ReportedIssue, error) {
// metadata, err := json.Marshal(issue.Metadata)
// if err != nil {
// return domain.ReportedIssue{}, err
// }
params := dbgen.CreateReportedIssueParams{
// Map fields from domain.ReportedIssue to dbgen.CreateReportedIssueParams here.
// Example:
// Title: issue.Title,
// Description: issue.Description,
// CustomerID: issue.CustomerID,
// Status: issue.Status,
// Add other fields as necessary.
UserID: userID,
UserRole: string(role),
Subject: issue.Subject,
Description: issue.Description,
IssueType: string(issue.IssueType),
// Metadata: metadata,
}
dbIssue, err := s.repo.CreateReportedIssue(ctx, params)
if err != nil {
@ -36,17 +40,20 @@ func (s *Service) CreateReportedIssue(ctx context.Context, issue domain.Reported
ID: dbIssue.ID,
Subject: dbIssue.Subject,
Description: dbIssue.Description,
CustomerID: dbIssue.CustomerID,
Status: dbIssue.Status,
UserID: dbIssue.UserID,
UserRole: domain.Role(dbIssue.UserRole),
Status: domain.ReportedIssueStatus(dbIssue.Status),
IssueType: domain.ReportedIssueType(dbIssue.IssueType),
CreatedAt: dbIssue.CreatedAt.Time,
UpdatedAt: dbIssue.UpdatedAt.Time,
// Add other fields as necessary
}
return reportedIssue, nil
}
func (s *Service) GetIssuesForCustomer(ctx context.Context, customerID int64, limit, offset int) ([]domain.ReportedIssue, error) {
dbIssues, err := s.repo.ListReportedIssuesByCustomer(ctx, customerID, int32(limit), int32(offset))
func (s *Service) GetIssuesForUser(ctx context.Context, userID int64, limit, offset int) ([]domain.ReportedIssue, error) {
dbIssues, err := s.repo.ListReportedIssuesByUser(ctx, userID, int32(limit), int32(offset))
if err != nil {
return nil, err
}
@ -56,8 +63,10 @@ func (s *Service) GetIssuesForCustomer(ctx context.Context, customerID int64, li
ID: dbIssue.ID,
Subject: dbIssue.Subject,
Description: dbIssue.Description,
CustomerID: dbIssue.CustomerID,
Status: dbIssue.Status,
UserID: dbIssue.UserID,
UserRole: domain.Role(dbIssue.UserRole),
Status: domain.ReportedIssueStatus(dbIssue.Status),
IssueType: domain.ReportedIssueType(dbIssue.IssueType),
CreatedAt: dbIssue.CreatedAt.Time,
UpdatedAt: dbIssue.UpdatedAt.Time,
// Add other fields as necessary

View File

@ -25,8 +25,8 @@ var (
ErrTicketAmountTooHigh = errors.New("Cannot create a ticket with an amount above limit")
ErrTicketLimitForSingleUser = errors.New("Number of Ticket Limit reached")
ErrTicketWinningTooHigh = errors.New("Total Winnings over set limit")
ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid")
ErrInvalidAmount = errors.New("Invalid amount")
ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid")
)
type Service struct {
@ -34,7 +34,7 @@ type Service struct {
eventSvc event.Service
prematchSvc odds.ServiceImpl
mongoLogger *zap.Logger
settingSvc settings.Service
settingSvc settings.Service
notificationSvc *notificationservice.Service
}
@ -51,7 +51,7 @@ func NewService(
eventSvc: eventSvc,
prematchSvc: prematchSvc,
mongoLogger: mongoLogger,
settingSvc: settingSvc,
settingSvc: settingSvc,
notificationSvc: notificationSvc,
}
}
@ -167,6 +167,11 @@ func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq,
}
if req.Amount < 1 {
return domain.Ticket{}, 0, ErrInvalidAmount
}
// Check to see if the amount is above a set limit
if req.Amount > settingsList.BetAmountLimit.Float32() {
return domain.Ticket{}, 0, ErrTicketAmountTooHigh
@ -183,7 +188,6 @@ func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq,
// Check to see how many tickets a single anonymous user has created
if count > settingsList.DailyTicketPerIP {
return domain.Ticket{}, 0, ErrTicketLimitForSingleUser
}

View File

@ -23,22 +23,22 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
spec string
task func()
}{
{
spec: "0 0 * * * *", // Every 1 hour
task: func() {
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
log.Printf("FetchUpcomingEvents error: %v", err)
}
},
},
{
spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
task: func() {
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
log.Printf("FetchNonLiveOdds error: %v", err)
}
},
},
// {
// spec: "0 0 * * * *", // Every 1 hour
// task: func() {
// if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
// log.Printf("FetchUpcomingEvents error: %v", err)
// }
// },
// },
// {
// spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
// task: func() {
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
// log.Printf("FetchNonLiveOdds error: %v", err)
// }
// },
// },
{
spec: "0 */5 * * * *", // Every 5 Minutes
task: func() {

View File

@ -10,61 +10,6 @@ import (
"github.com/gofiber/fiber/v2"
)
// GetALLPrematchOdds
// @Summary Retrieve all prematch odds
// @Description Retrieve all prematch odds from the database
// @Tags prematch
// @Accept json
// @Produce json
// @Success 200 {array} domain.Odd
// @Failure 500 {object} response.APIResponse
// @Router /odds [get]
func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error {
odds, err := h.prematchSvc.GetALLPrematchOdds(c.Context())
if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve all prematch odds", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "All prematch odds retrieved successfully", odds, nil)
}
// GetRawOddsByMarketID
// @Summary Retrieve raw odds by Market ID
// @Description Retrieve raw odds records using a Market ID
// @Tags prematch
// @Accept json
// @Produce json
// @Param upcoming_id path string true "Upcoming ID"
// @Param market_id path string true "Market ID"
// @Success 200 {array} domain.RawOddsByMarketID
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /odds/upcoming/{upcoming_id}/market/{market_id} [get]
func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error {
marketID := c.Params("market_id")
upcomingID := c.Params("upcoming_id")
if marketID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing market_id", nil, nil)
}
if upcomingID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing upcoming_id", nil, nil)
}
rawOdds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketID, upcomingID)
if err != nil {
// fmt.Printf("Failed to fetch raw odds: %v market_id:%v upcomingID:%v\n", err, marketID, upcomingID)
h.logger.Error("Failed to get raw odds by market ID", "marketID", marketID, "upcomingID", upcomingID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve raw odds", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", rawOdds, nil)
}
// @Summary Retrieve all upcoming events
// @Description Retrieve all upcoming events from the database
// @Tags prematch
@ -151,6 +96,10 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
Value: countryCodeQuery,
Valid: countryCodeQuery != "",
}
flaggedQuery := c.Query("flagged")
if flaggedQuery != "" &&
events, total, err := h.eventSvc.GetPaginatedUpcomingEvents(
c.Context(), domain.EventFilter{
SportID: sportID,
@ -257,44 +206,6 @@ func (h *Handler) GetUpcomingEventByID(c *fiber.Ctx) error {
}
// @Summary Retrieve prematch odds by upcoming ID (FI)
// @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination
// @Tags prematch
// @Accept json
// @Produce json
// @Param upcoming_id path string true "Upcoming Event ID (FI)"
// @Param limit query int false "Number of results to return (default: 10)"
// @Param offset query int false "Number of results to skip (default: 0)"
// @Success 200 {array} domain.Odd
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /odds/upcoming/{upcoming_id} [get]
func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error {
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing upcoming_id", nil, nil)
}
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid limit value", nil, nil)
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid offset value", nil, nil)
}
odds, err := h.prematchSvc.GetPrematchOddsByUpcomingID(c.Context(), upcomingID)
if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve prematch odds", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil)
}
type UpdateEventStatusReq struct {
}
@ -320,3 +231,45 @@ func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil)
}
type UpdateEventFlaggedReq struct {
Flagged bool `json:"flagged" example:"true"`
}
// UpdateEventFlagged godoc
// @Summary update the event flagged
// @Description Update the event flagged
// @Tags event
// @Accept json
// @Produce json
// @Param id path int true "Event ID"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /events/{id}/flag [put]
func (h *Handler) UpdateEventFlagged(c *fiber.Ctx) error {
eventID := c.Params("id")
var req UpdateEventFlaggedReq
if err := c.BodyParser(&req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err := h.eventSvc.UpdateFlagged(c.Context(), eventID, req.Flagged)
if err != nil {
h.logger.Error("Failed to update event flagged", "eventID", eventID, "error", err)
}
return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil)
}

View File

@ -19,12 +19,15 @@ import (
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/issues [post]
func (h *Handler) CreateIssue(c *fiber.Ctx) error {
var req domain.ReportedIssue
role := c.Locals("role").(domain.Role)
userID := c.Locals("user_id").(int64)
var req domain.ReportedIssueReq
if err := c.BodyParser(&req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
}
created, err := h.issueReportingSvc.CreateReportedIssue(c.Context(), req)
created, err := h.issueReportingSvc.CreateReportedIssue(c.Context(), req, userID, role)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
@ -32,27 +35,27 @@ func (h *Handler) CreateIssue(c *fiber.Ctx) error {
return c.Status(fiber.StatusCreated).JSON(created)
}
// GetCustomerIssues godoc
// @Summary Get reported issues by a customer
// @Description Returns all issues reported by a specific customer
// GetUserIssues godoc
// @Summary Get reported issues by a user
// @Description Returns all issues reported by a specific user
// @Tags Issues
// @Produce json
// @Param customer_id path int true "Customer ID"
// @Param user_id path int true "User ID"
// @Param limit query int false "Limit"
// @Param offset query int false "Offset"
// @Success 200 {array} domain.ReportedIssue
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/issues/customer/{customer_id} [get]
func (h *Handler) GetCustomerIssues(c *fiber.Ctx) error {
customerID, err := strconv.ParseInt(c.Params("customer_id"), 10, 64)
// @Router /api/v1/issues/user/{user_id} [get]
func (h *Handler) GetUserIssues(c *fiber.Ctx) error {
userID, err := strconv.ParseInt(c.Params("user_id"), 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid customer ID")
return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
}
limit, offset := getPaginationParams(c)
issues, err := h.issueReportingSvc.GetIssuesForCustomer(c.Context(), customerID, limit, offset)
issues, err := h.issueReportingSvc.GetIssuesForUser(c.Context(), userID, limit, offset)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}

View File

@ -0,0 +1,102 @@
package handlers
import (
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
)
// GetALLPrematchOdds
// @Summary Retrieve all prematch odds
// @Description Retrieve all prematch odds from the database
// @Tags prematch
// @Accept json
// @Produce json
// @Success 200 {array} domain.Odd
// @Failure 500 {object} response.APIResponse
// @Router /odds [get]
func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error {
odds, err := h.prematchSvc.GetALLPrematchOdds(c.Context())
if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve all prematch odds", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "All prematch odds retrieved successfully", odds, nil)
}
// GetRawOddsByMarketID
// @Summary Retrieve raw odds by Market ID
// @Description Retrieve raw odds records using a Market ID
// @Tags prematch
// @Accept json
// @Produce json
// @Param upcoming_id path string true "Upcoming ID"
// @Param market_id path string true "Market ID"
// @Success 200 {array} domain.RawOddsByMarketID
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /odds/upcoming/{upcoming_id}/market/{market_id} [get]
func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error {
marketID := c.Params("market_id")
upcomingID := c.Params("upcoming_id")
if marketID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing market_id", nil, nil)
}
if upcomingID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing upcoming_id", nil, nil)
}
rawOdds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketID, upcomingID)
if err != nil {
// fmt.Printf("Failed to fetch raw odds: %v market_id:%v upcomingID:%v\n", err, marketID, upcomingID)
h.logger.Error("Failed to get raw odds by market ID", "marketID", marketID, "upcomingID", upcomingID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve raw odds", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", rawOdds, nil)
}
// @Summary Retrieve prematch odds by upcoming ID (FI)
// @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination
// @Tags prematch
// @Accept json
// @Produce json
// @Param upcoming_id path string true "Upcoming Event ID (FI)"
// @Param limit query int false "Number of results to return (default: 10)"
// @Param offset query int false "Number of results to skip (default: 0)"
// @Success 200 {array} domain.Odd
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /odds/upcoming/{upcoming_id} [get]
func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error {
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing upcoming_id", nil, nil)
}
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid limit value", nil, nil)
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid offset value", nil, nil)
}
odds, err := h.prematchSvc.GetPrematchOddsByUpcomingID(c.Context(), upcomingID)
if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve prematch odds", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil)
}

View File

@ -61,6 +61,12 @@ func (a *App) initAppRoutes() {
})
})
group.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "FortuneBet API V1 pre-alpha",
"version": "1.0dev9",
})
})
// Auth Routes
a.fiber.Post("/auth/login", h.LoginCustomer)
a.fiber.Post("/auth/refresh", h.RefreshToken)
@ -146,6 +152,7 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/events/:id", h.GetUpcomingEventByID)
a.fiber.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved)
a.fiber.Get("/top-leagues", h.GetTopLeagues)
a.fiber.Get("/events/:id/flag", h.UpdateEventFlagged)
// Leagues
a.fiber.Get("/leagues", h.GetAllLeagues)
@ -315,8 +322,8 @@ func (a *App) initAppRoutes() {
group.Get("/virtual-game/favorites", a.authMiddleware, h.ListFavorites)
//Issue Reporting Routes
group.Post("/issues", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateIssue)
group.Get("/issues/customer/:customer_id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetCustomerIssues)
group.Post("/issues", a.authMiddleware, h.CreateIssue) //anyone who has logged can report a
group.Get("/issues/customer/:customer_id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetUserIssues)
group.Get("/issues", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllIssues)
group.Patch("/issues/:issue_id/status", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateIssueStatus)
group.Delete("/issues/:issue_id", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteIssue)

View File

@ -47,6 +47,7 @@ postgres:
backup:
@mkdir -p backup
@docker exec -t fortunebet-backend-postgres-1 pg_dumpall -c -U root | gzip > backup/dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.gz
restore:
@echo "Restoring latest backup..."
gunzip -c $(file) | docker exec -i fortunebet-backend-postgres-1 psql -U root -d gh