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,
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
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,
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
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,31 +16,21 @@ 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"`
UserID int64 `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"`
}
@ -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,12 +280,12 @@ 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"`
UserID int64 `json:"user_id"`
OutcomesHash string `json:"outcomes_hash"`
}

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
user_id,
user_role,
subject,
description,
issue_type,
metadata
)
RETURNING id, customer_id, subject, description, issue_type, status, metadata, created_at, updated_at
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"`
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,
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
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,
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
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
UserID int64
IsShopBet bool
CashedOut bool
CashoutID string
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
}
@ -109,9 +95,6 @@ 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"`
}
type RandomBetReq struct {
@ -124,13 +107,9 @@ 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"`
@ -138,14 +117,12 @@ type BetRes 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"`
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"`
CashedID string `json:"cashed_id" example:"21234"`
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,12 +131,8 @@ 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,
}
}
@ -169,14 +142,12 @@ func ConvertBet(bet GetBet) BetRes {
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
BranchID: bet.BranchID.Value,
UserID: bet.UserID.Value,
Fullname: bet.FullName,
UserID: bet.UserID,
Outcomes: bet.Outcomes,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CashedID: bet.CashoutID,
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

@ -25,23 +25,10 @@ func convertDBBet(bet dbgen.Bet) domain.Bet {
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,
},
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CashoutID: bet.CashoutID,
FastCode: bet.FastCode,
CreatedAt: bet.CreatedAt.Time,
}
}
@ -78,20 +65,13 @@ 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,
},
FullName: bet.FullName.(string),
PhoneNumber: bet.PhoneNumber.String,
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CashoutID: bet.CashoutID,
Outcomes: outcomes,
FastCode: bet.FastCode,
CreatedAt: bet.CreatedAt.Time,
}
}
@ -122,19 +102,8 @@ func convertCreateBet(bet domain.CreateBet) 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,
},
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,
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,6 +38,9 @@ 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 {
@ -44,6 +49,8 @@ type Service struct {
prematchSvc odds.ServiceImpl
walletSvc wallet.Service
branchSvc branch.Service
settingSvc settings.Service
notificationSvc *notificationservice.Service
logger *slog.Logger
mongoLogger *zap.Logger
}
@ -54,6 +61,8 @@ func NewService(
prematchSvc odds.ServiceImpl,
walletSvc wallet.Service,
branchSvc branch.Service,
settingSvc settings.Service,
notificationSvc *notificationservice.Service,
logger *slog.Logger,
mongoLogger *zap.Logger,
) *Service {
@ -63,6 +72,8 @@ func NewService(
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,7 +25,7 @@ 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")
ErrInvalidAmount = errors.New("Invalid amount")
ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid")
)
@ -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