feat: Enhance league, odds, events and bets functionality

- Updated league handling to ensure valid page size checks and improved error handling for sport ID parsing.
- Introduced new endpoint to update global league settings with comprehensive validation and error logging.
- Refactored odds settings management, including saving, removing, and updating odds settings with enhanced validation.
- Added tenant slug retrieval by token, ensuring proper user and company validation.
- Improved middleware to check for active company status and adjusted route permissions for various endpoints.
- Added SQL script to fix auto-increment desynchronization across multiple tables.
This commit is contained in:
Samuel Tariku 2025-10-05 23:45:31 +03:00
parent e401d1d82f
commit c00110a503
44 changed files with 1672 additions and 316 deletions

View File

@ -75,8 +75,6 @@ CREATE TABLE IF NOT EXISTS wallets (
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, type)
);
CREATE TABLE refresh_tokens (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
@ -342,7 +340,7 @@ CREATE TABLE company_event_settings (
event_id BIGINT NOT NULL,
is_active BOOLEAN,
is_featured BOOLEAN,
winning_upper_limit INT,
winning_upper_limit BIGINT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (company_id, event_id)
);

View File

@ -133,6 +133,38 @@ SELECT *
FROM bet_with_outcomes
WHERE status = 2
AND processed = false;
-- name: GetBetOutcomeViewByEventID :many
SELECT bet_outcomes.*,
users.first_name,
users.last_name,
bets.amount,
bets.total_odds
FROM bet_outcomes
JOIN bets ON bets.id = bet_outcomes.bet_id
JOIN users ON bets.user_id = users.id
WHERE bet_outcomes.event_id = $1
AND (
bets.company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
bet_outcomes.status = sqlc.narg('filter_status')
OR sqlc.narg('filter_status') IS NULL
)
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: TotalBetOutcomeViewByEventID :one
SELECT count(*)
FROM bet_outcomes
JOIN bets ON bets.id = bet_outcomes.bet_id
WHERE bet_outcomes.event_id = $1
AND (
bets.company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
bet_outcomes.status = sqlc.narg('filter_status')
OR sqlc.narg('filter_status') IS NULL
);
-- name: GetBetOutcomeByEventID :many
SELECT *
FROM bet_outcomes
@ -180,6 +212,11 @@ UPDATE bet_outcomes
SEt status = $1
WHERE event_id = $2
RETURNING *;
-- name: UpdateBetOutcomeStatusForOddID :many
UPDATE bet_outcomes
SEt status = $1
WHERE odd_id = $2
RETURNING *;
-- name: UpdateStatus :exec
UPDATE bets
SET status = $1,

View File

@ -30,8 +30,8 @@ WHERE (
SELECT *
FROM companies_details
WHERE id = $1;
-- name: GetCompanyIDUsingSlug :one
SELECT id
-- name: GetCompanyUsingSlug :one
SELECT *
FROM companies
WHERE slug = $1;
-- name: SearchCompanyByName :many

View File

@ -56,7 +56,7 @@ SET sport_id = EXCLUDED.sport_id,
source = EXCLUDED.source,
default_winning_upper_limit = EXCLUDED.default_winning_upper_limit,
fetched_at = now();
-- name: SaveEventSettings :exec
-- name: SaveTenantEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
@ -300,7 +300,9 @@ FROM events e
WHERE e.id = $1
LIMIT 1;
-- name: GetSportAndLeagueIDs :one
SELECT sport_id, league_id FROM events
SELECT sport_id,
league_id
FROM events
WHERE id = $1;
-- name: UpdateMatchResult :exec
UPDATE events
@ -313,8 +315,22 @@ FROM events
WHERE id = $1;
-- name: UpdateEventMonitored :exec
UPDATE events
SET is_monitored = $1
SET is_monitored = $1,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2;
-- name: UpdateGlobalEventSettings :exec
UPDATE events
SET default_is_active = COALESCE(sqlc.narg(default_is_active), default_is_active),
default_is_featured = COALESCE(
sqlc.narg(default_is_featured),
default_is_featured
),
default_winning_upper_limit = COALESCE(
sqlc.narg(default_winning_upper_limit),
default_winning_upper_limit
),
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;
-- name: DeleteEvent :exec
DELETE FROM events
WHERE id = $1;
WHERE id = $1;

View File

@ -14,7 +14,7 @@ SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id,
sport_id = EXCLUDED.sport_id;
-- name: InsertLeagueSettings :exec
-- name: SaveLeagueSettings :exec
INSERT INTO company_league_settings (
company_id,
league_id,
@ -118,7 +118,7 @@ SET name = COALESCE(sqlc.narg('name'), name),
bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id),
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
WHERE id = $1;
-- name: UpdateLeagueSettings :exec
-- name: UpdateCompanyLeagueSettings :exec
UPDATE company_league_settings
SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured = COALESCE(
@ -126,4 +126,9 @@ SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured
)
WHERE league_id = $1
AND company_id = $2;
AND company_id = $2;
-- name: UpdateGlobalLeagueSettings :exec
UPDATE leagues
SET default_is_active = COALESCE(sqlc.narg('is_active'), default_is_active),
default_is_featured = COALESCE(sqlc.narg('is_featured'), default_is_featured)
WHERE id = $1;

View File

@ -143,4 +143,15 @@ WHERE event_id = $1
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: DeleteOddsForEvent :exec
DELETE FROM odds_market
Where event_id = $1;
Where event_id = $1;
-- name: DeleteAllCompanyOddsSetting :exec
DELETE FROM company_odd_settings
WHERE company_id = $1;
-- name: DeleteCompanyOddsSettingByOddMarketID :exec
DELETE FROM company_odd_settings
WHERE company_id = $1
AND odds_market_id = $2;
-- name: UpdateGlobalOddsSetting :exec
UPDATE odds_market
SET default_is_active = COALESCE(sqlc.narg(default_is_active), default_is_active)
WHERE id = $1;

View File

@ -0,0 +1,31 @@
-- For each table with an id sequence
SELECT setval(
pg_get_serial_sequence('users', 'id'),
COALESCE(MAX(id), 1)
)
FROM users;
SELECT setval(
pg_get_serial_sequence('wallets', 'id'),
COALESCE(MAX(id), 1)
)
FROM wallets;
SELECT setval(
pg_get_serial_sequence('customer_wallets', 'id'),
COALESCE(MAX(id), 1)
)
FROM customer_wallets;
SELECT setval(
pg_get_serial_sequence('companies', 'id'),
COALESCE(MAX(id), 1)
)
FROM companies;
SELECT setval(
pg_get_serial_sequence('branches', 'id'),
COALESCE(MAX(id), 1)
)
FROM branches;
SELECT setval(
pg_get_serial_sequence('supported_operations', 'id'),
COALESCE(MAX(id), 1)
)
FROM supported_operations;

View File

@ -448,6 +448,103 @@ func (q *Queries) GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (i
return count, err
}
const GetBetOutcomeViewByEventID = `-- name: GetBetOutcomeViewByEventID :many
SELECT bet_outcomes.id, bet_outcomes.bet_id, bet_outcomes.sport_id, bet_outcomes.event_id, bet_outcomes.odd_id, bet_outcomes.home_team_name, bet_outcomes.away_team_name, bet_outcomes.market_id, bet_outcomes.market_name, bet_outcomes.odd, bet_outcomes.odd_name, bet_outcomes.odd_header, bet_outcomes.odd_handicap, bet_outcomes.status, bet_outcomes.expires,
users.first_name,
users.last_name,
bets.amount,
bets.total_odds
FROM bet_outcomes
JOIN bets ON bets.id = bet_outcomes.bet_id
JOIN users ON bets.user_id = users.id
WHERE bet_outcomes.event_id = $1
AND (
bets.company_id = $2
OR $2 IS NULL
)
AND (
bet_outcomes.status = $3
OR $3 IS NULL
)
LIMIT $5 OFFSET $4
`
type GetBetOutcomeViewByEventIDParams struct {
EventID int64 `json:"event_id"`
CompanyID pgtype.Int8 `json:"company_id"`
FilterStatus pgtype.Int4 `json:"filter_status"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
type GetBetOutcomeViewByEventIDRow struct {
ID int64 `json:"id"`
BetID int64 `json:"bet_id"`
SportID int64 `json:"sport_id"`
EventID int64 `json:"event_id"`
OddID int64 `json:"odd_id"`
HomeTeamName string `json:"home_team_name"`
AwayTeamName string `json:"away_team_name"`
MarketID int64 `json:"market_id"`
MarketName string `json:"market_name"`
Odd float32 `json:"odd"`
OddName string `json:"odd_name"`
OddHeader string `json:"odd_header"`
OddHandicap string `json:"odd_handicap"`
Status int32 `json:"status"`
Expires pgtype.Timestamp `json:"expires"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
}
func (q *Queries) GetBetOutcomeViewByEventID(ctx context.Context, arg GetBetOutcomeViewByEventIDParams) ([]GetBetOutcomeViewByEventIDRow, error) {
rows, err := q.db.Query(ctx, GetBetOutcomeViewByEventID,
arg.EventID,
arg.CompanyID,
arg.FilterStatus,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetBetOutcomeViewByEventIDRow
for rows.Next() {
var i GetBetOutcomeViewByEventIDRow
if err := rows.Scan(
&i.ID,
&i.BetID,
&i.SportID,
&i.EventID,
&i.OddID,
&i.HomeTeamName,
&i.AwayTeamName,
&i.MarketID,
&i.MarketName,
&i.Odd,
&i.OddName,
&i.OddHeader,
&i.OddHandicap,
&i.Status,
&i.Expires,
&i.FirstName,
&i.LastName,
&i.Amount,
&i.TotalOdds,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetBetsForCashback = `-- name: GetBetsForCashback :many
SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes
FROM bet_with_outcomes
@ -557,6 +654,34 @@ func (q *Queries) GetTotalBets(ctx context.Context, arg GetTotalBetsParams) (int
return count, err
}
const TotalBetOutcomeViewByEventID = `-- name: TotalBetOutcomeViewByEventID :one
SELECT count(*)
FROM bet_outcomes
JOIN bets ON bets.id = bet_outcomes.bet_id
WHERE bet_outcomes.event_id = $1
AND (
bets.company_id = $2
OR $2 IS NULL
)
AND (
bet_outcomes.status = $3
OR $3 IS NULL
)
`
type TotalBetOutcomeViewByEventIDParams struct {
EventID int64 `json:"event_id"`
CompanyID pgtype.Int8 `json:"company_id"`
FilterStatus pgtype.Int4 `json:"filter_status"`
}
func (q *Queries) TotalBetOutcomeViewByEventID(ctx context.Context, arg TotalBetOutcomeViewByEventIDParams) (int64, error) {
row := q.db.QueryRow(ctx, TotalBetOutcomeViewByEventID, arg.EventID, arg.CompanyID, arg.FilterStatus)
var count int64
err := row.Scan(&count)
return count, err
}
const UpdateBetOutcomeStatus = `-- name: UpdateBetOutcomeStatus :one
UPDATE bet_outcomes
SET status = $1
@ -675,6 +800,54 @@ func (q *Queries) UpdateBetOutcomeStatusForEvent(ctx context.Context, arg Update
return items, nil
}
const UpdateBetOutcomeStatusForOddID = `-- name: UpdateBetOutcomeStatusForOddID :many
UPDATE bet_outcomes
SEt status = $1
WHERE odd_id = $2
RETURNING id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires
`
type UpdateBetOutcomeStatusForOddIDParams struct {
Status int32 `json:"status"`
OddID int64 `json:"odd_id"`
}
func (q *Queries) UpdateBetOutcomeStatusForOddID(ctx context.Context, arg UpdateBetOutcomeStatusForOddIDParams) ([]BetOutcome, error) {
rows, err := q.db.Query(ctx, UpdateBetOutcomeStatusForOddID, arg.Status, arg.OddID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []BetOutcome
for rows.Next() {
var i BetOutcome
if err := rows.Scan(
&i.ID,
&i.BetID,
&i.SportID,
&i.EventID,
&i.OddID,
&i.HomeTeamName,
&i.AwayTeamName,
&i.MarketID,
&i.MarketName,
&i.Odd,
&i.OddName,
&i.OddHeader,
&i.OddHandicap,
&i.Status,
&i.Expires,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateBetWithCashback = `-- name: UpdateBetWithCashback :exec
UPDATE bets
SET processed = $1

View File

@ -153,17 +153,27 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail
return i, err
}
const GetCompanyIDUsingSlug = `-- name: GetCompanyIDUsingSlug :one
SELECT id
const GetCompanyUsingSlug = `-- name: GetCompanyUsingSlug :one
SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at
FROM companies
WHERE slug = $1
`
func (q *Queries) GetCompanyIDUsingSlug(ctx context.Context, slug string) (int64, error) {
row := q.db.QueryRow(ctx, GetCompanyIDUsingSlug, slug)
var id int64
err := row.Scan(&id)
return id, err
func (q *Queries) GetCompanyUsingSlug(ctx context.Context, slug string) (Company, error) {
row := q.db.QueryRow(ctx, GetCompanyUsingSlug, slug)
var i Company
err := row.Scan(
&i.ID,
&i.Name,
&i.Slug,
&i.AdminID,
&i.WalletID,
&i.DeductedPercentage,
&i.IsActive,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const SearchCompanyByName = `-- name: SearchCompanyByName :many

View File

@ -282,7 +282,7 @@ type GetEventWithSettingByIDRow struct {
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
WinningUpperLimit int64 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
LeagueCc pgtype.Text `json:"league_cc"`
}
@ -440,7 +440,7 @@ type GetEventsWithSettingsRow struct {
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
WinningUpperLimit int64 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
LeagueCc pgtype.Text `json:"league_cc"`
}
@ -514,7 +514,9 @@ func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSe
}
const GetSportAndLeagueIDs = `-- name: GetSportAndLeagueIDs :one
SELECT sport_id, league_id FROM events
SELECT sport_id,
league_id
FROM events
WHERE id = $1
`
@ -831,7 +833,7 @@ func (q *Queries) ListLiveEvents(ctx context.Context) ([]int64, error) {
return items, nil
}
const SaveEventSettings = `-- name: SaveEventSettings :exec
const SaveTenantEventSettings = `-- name: SaveTenantEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
@ -846,16 +848,16 @@ SET is_active = EXCLUDED.is_active,
winning_upper_limit = EXCLUDED.winning_upper_limit
`
type SaveEventSettingsParams struct {
type SaveTenantEventSettingsParams struct {
CompanyID int64 `json:"company_id"`
EventID int64 `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
WinningUpperLimit pgtype.Int8 `json:"winning_upper_limit"`
}
func (q *Queries) SaveEventSettings(ctx context.Context, arg SaveEventSettingsParams) error {
_, err := q.db.Exec(ctx, SaveEventSettings,
func (q *Queries) SaveTenantEventSettings(ctx context.Context, arg SaveTenantEventSettingsParams) error {
_, err := q.db.Exec(ctx, SaveTenantEventSettings,
arg.CompanyID,
arg.EventID,
arg.IsActive,
@ -867,7 +869,8 @@ func (q *Queries) SaveEventSettings(ctx context.Context, arg SaveEventSettingsPa
const UpdateEventMonitored = `-- name: UpdateEventMonitored :exec
UPDATE events
SET is_monitored = $1
SET is_monitored = $1,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2
`
@ -881,6 +884,38 @@ func (q *Queries) UpdateEventMonitored(ctx context.Context, arg UpdateEventMonit
return err
}
const UpdateGlobalEventSettings = `-- name: UpdateGlobalEventSettings :exec
UPDATE events
SET default_is_active = COALESCE($2, default_is_active),
default_is_featured = COALESCE(
$3,
default_is_featured
),
default_winning_upper_limit = COALESCE(
$4,
default_winning_upper_limit
),
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`
type UpdateGlobalEventSettingsParams struct {
ID int64 `json:"id"`
DefaultIsActive pgtype.Bool `json:"default_is_active"`
DefaultIsFeatured pgtype.Bool `json:"default_is_featured"`
DefaultWinningUpperLimit pgtype.Int8 `json:"default_winning_upper_limit"`
}
func (q *Queries) UpdateGlobalEventSettings(ctx context.Context, arg UpdateGlobalEventSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateGlobalEventSettings,
arg.ID,
arg.DefaultIsActive,
arg.DefaultIsFeatured,
arg.DefaultWinningUpperLimit,
)
return err
}
const UpdateMatchResult = `-- name: UpdateMatchResult :exec
UPDATE events
SET score = $1,

View File

@ -292,7 +292,7 @@ func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) erro
return err
}
const InsertLeagueSettings = `-- name: InsertLeagueSettings :exec
const SaveLeagueSettings = `-- name: SaveLeagueSettings :exec
INSERT INTO company_league_settings (
company_id,
league_id,
@ -305,15 +305,15 @@ SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured
`
type InsertLeagueSettingsParams struct {
type SaveLeagueSettingsParams struct {
CompanyID int64 `json:"company_id"`
LeagueID int64 `json:"league_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) InsertLeagueSettings(ctx context.Context, arg InsertLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, InsertLeagueSettings,
func (q *Queries) SaveLeagueSettings(ctx context.Context, arg SaveLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, SaveLeagueSettings,
arg.CompanyID,
arg.LeagueID,
arg.IsActive,
@ -322,6 +322,52 @@ func (q *Queries) InsertLeagueSettings(ctx context.Context, arg InsertLeagueSett
return err
}
const UpdateCompanyLeagueSettings = `-- name: UpdateCompanyLeagueSettings :exec
UPDATE company_league_settings
SET is_active = COALESCE($3, is_active),
is_featured = COALESCE(
$4,
is_featured
)
WHERE league_id = $1
AND company_id = $2
`
type UpdateCompanyLeagueSettingsParams struct {
LeagueID int64 `json:"league_id"`
CompanyID int64 `json:"company_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) UpdateCompanyLeagueSettings(ctx context.Context, arg UpdateCompanyLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateCompanyLeagueSettings,
arg.LeagueID,
arg.CompanyID,
arg.IsActive,
arg.IsFeatured,
)
return err
}
const UpdateGlobalLeagueSettings = `-- name: UpdateGlobalLeagueSettings :exec
UPDATE leagues
SET default_is_active = COALESCE($2, default_is_active),
default_is_featured = COALESCE($3, default_is_featured)
WHERE id = $1
`
type UpdateGlobalLeagueSettingsParams struct {
ID int64 `json:"id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) UpdateGlobalLeagueSettings(ctx context.Context, arg UpdateGlobalLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateGlobalLeagueSettings, arg.ID, arg.IsActive, arg.IsFeatured)
return err
}
const UpdateLeague = `-- name: UpdateLeague :exec
UPDATE leagues
SET name = COALESCE($2, name),
@ -349,31 +395,3 @@ func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) erro
)
return err
}
const UpdateLeagueSettings = `-- name: UpdateLeagueSettings :exec
UPDATE company_league_settings
SET is_active = COALESCE($3, is_active),
is_featured = COALESCE(
$4,
is_featured
)
WHERE league_id = $1
AND company_id = $2
`
type UpdateLeagueSettingsParams struct {
LeagueID int64 `json:"league_id"`
CompanyID int64 `json:"company_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) UpdateLeagueSettings(ctx context.Context, arg UpdateLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateLeagueSettings,
arg.LeagueID,
arg.CompanyID,
arg.IsActive,
arg.IsFeatured,
)
return err
}

View File

@ -177,7 +177,7 @@ type CompanyEventSetting struct {
EventID int64 `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
WinningUpperLimit pgtype.Int8 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
@ -405,7 +405,7 @@ type EventWithSetting struct {
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
WinningUpperLimit int64 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
LeagueCc pgtype.Text `json:"league_cc"`
}

View File

@ -11,6 +11,32 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
const DeleteAllCompanyOddsSetting = `-- name: DeleteAllCompanyOddsSetting :exec
DELETE FROM company_odd_settings
WHERE company_id = $1
`
func (q *Queries) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error {
_, err := q.db.Exec(ctx, DeleteAllCompanyOddsSetting, companyID)
return err
}
const DeleteCompanyOddsSettingByOddMarketID = `-- name: DeleteCompanyOddsSettingByOddMarketID :exec
DELETE FROM company_odd_settings
WHERE company_id = $1
AND odds_market_id = $2
`
type DeleteCompanyOddsSettingByOddMarketIDParams struct {
CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
}
func (q *Queries) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, arg DeleteCompanyOddsSettingByOddMarketIDParams) error {
_, err := q.db.Exec(ctx, DeleteCompanyOddsSettingByOddMarketID, arg.CompanyID, arg.OddsMarketID)
return err
}
const DeleteOddsForEvent = `-- name: DeleteOddsForEvent :exec
DELETE FROM odds_market
Where event_id = $1
@ -568,3 +594,19 @@ func (q *Queries) SaveOddSettings(ctx context.Context, arg SaveOddSettingsParams
)
return err
}
const UpdateGlobalOddsSetting = `-- name: UpdateGlobalOddsSetting :exec
UPDATE odds_market
SET default_is_active = COALESCE($2, default_is_active)
WHERE id = $1
`
type UpdateGlobalOddsSettingParams struct {
ID int64 `json:"id"`
DefaultIsActive pgtype.Bool `json:"default_is_active"`
}
func (q *Queries) UpdateGlobalOddsSetting(ctx context.Context, arg UpdateGlobalOddsSettingParams) error {
_, err := q.db.Exec(ctx, UpdateGlobalOddsSetting, arg.ID, arg.DefaultIsActive)
return err
}

View File

@ -163,6 +163,35 @@ type BetRes struct {
FastCode string `json:"fast_code"`
}
type BetOutcomeViewRes struct {
ID int64 `json:"id"`
BetID int64 `json:"bet_id"`
SportID int64 `json:"sport_id"`
EventID int64 `json:"event_id"`
OddID int64 `json:"odd_id"`
HomeTeamName string `json:"home_team_name"`
AwayTeamName string `json:"away_team_name"`
MarketID int64 `json:"market_id"`
MarketName string `json:"market_name"`
Odd float32 `json:"odd"`
OddName string `json:"odd_name"`
OddHeader string `json:"odd_header"`
OddHandicap string `json:"odd_handicap"`
Status OutcomeStatus `json:"status"`
Expires time.Time `json:"expires"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
}
type BetOutcomeViewFilter struct {
OutcomeStatus ValidOutcomeStatus
CompanyID ValidInt64
Limit ValidInt32
Offset ValidInt32
}
func ConvertCreateBetRes(bet Bet, createdNumber int64) CreateBetRes {
return CreateBetRes{
ID: bet.ID,
@ -228,6 +257,29 @@ func ConvertDBBetOutcomes(outcome dbgen.BetOutcome) BetOutcome {
Expires: outcome.Expires.Time,
}
}
func ConvertDBBetOutcomesView(outcome dbgen.GetBetOutcomeViewByEventIDRow) BetOutcomeViewRes {
return BetOutcomeViewRes{
ID: outcome.ID,
BetID: outcome.BetID,
SportID: outcome.SportID,
EventID: outcome.EventID,
OddID: outcome.OddID,
HomeTeamName: outcome.HomeTeamName,
AwayTeamName: outcome.AwayTeamName,
MarketID: outcome.MarketID,
MarketName: outcome.MarketName,
Odd: outcome.Odd,
OddName: outcome.OddName,
OddHeader: outcome.OddHeader,
OddHandicap: outcome.OddHandicap,
Status: OutcomeStatus(outcome.Status),
Expires: outcome.Expires.Time,
FirstName: outcome.FirstName,
LastName: outcome.LastName,
Amount: outcome.Amount,
TotalOdds: outcome.TotalOdds,
}
}
func ConvertDBBetWithOutcomes(bet dbgen.BetWithOutcome) GetBet {
var outcomes []BetOutcome = make([]BetOutcome, 0, len(bet.Outcomes))

View File

@ -1,6 +1,7 @@
package domain
import (
"fmt"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
@ -50,6 +51,58 @@ const (
EVENT_SOURCE_ENET EventSource = "enetpulse"
)
// --- EventStatus Validation ---
func (s EventStatus) IsValid() bool {
switch s {
case STATUS_PENDING,
STATUS_IN_PLAY,
STATUS_TO_BE_FIXED,
STATUS_ENDED,
STATUS_POSTPONED,
STATUS_CANCELLED,
STATUS_WALKOVER,
STATUS_INTERRUPTED,
STATUS_ABANDONED,
STATUS_RETIRED,
STATUS_SUSPENDED,
STATUS_DECIDED_BY_FA,
STATUS_REMOVED:
return true
default:
return false
}
}
func ParseEventStatus(val string) (EventStatus, error) {
s := EventStatus(val)
if !s.IsValid() {
return "", fmt.Errorf("invalid EventStatus: %q", val)
}
return s, nil
}
// --- EventSource Validation ---
func (s EventSource) IsValid() bool {
switch s {
case EVENT_SOURCE_BET365,
EVENT_SOURCE_BWIN,
EVENT_SOURCE_BETFAIR,
EVENT_SOURCE_1XBET,
EVENT_SOURCE_ENET:
return true
default:
return false
}
}
func ParseEventSource(val string) (EventSource, error) {
s := EventSource(val)
if !s.IsValid() {
return "", fmt.Errorf("invalid EventSource: %q", val)
}
return s, nil
}
type BaseEvent struct {
ID int64
SourceEventID string
@ -128,7 +181,7 @@ type EventWithSettings struct {
IsMonitored bool
IsFeatured bool
IsActive bool
WinningUpperLimit int32
WinningUpperLimit int64
DefaultIsFeatured bool
DefaultIsActive bool
DefaultWinningUpperLimit int64
@ -181,7 +234,7 @@ type EventWithSettingsRes struct {
IsMonitored bool `json:"is_monitored"`
IsFeatured bool `json:"is_featured"`
IsActive bool `json:"is_active"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
WinningUpperLimit int64 `json:"winning_upper_limit"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
@ -204,12 +257,18 @@ type EventSettings struct {
UpdatedAt time.Time
}
type CreateEventSettings struct {
type UpdateTenantEventSettings struct {
CompanyID int64
EventID int64
IsActive ValidBool
IsFeatured ValidBool
WinningUpperLimit ValidInt
WinningUpperLimit ValidInt64
}
type UpdateGlobalEventSettings struct {
EventID int64
IsActive ValidBool
IsFeatured ValidBool
WinningUpperLimit ValidInt64
}
type ValidEventStatus struct {
@ -331,8 +390,8 @@ func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
}
}
func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.SaveEventSettingsParams {
return dbgen.SaveEventSettingsParams{
func ConvertCreateEventSettings(eventSettings UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams {
return dbgen.SaveTenantEventSettingsParams{
CompanyID: eventSettings.CompanyID,
EventID: eventSettings.EventID,
IsActive: eventSettings.IsActive.ToPG(),
@ -343,17 +402,19 @@ func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.SaveEve
func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings {
return EventWithSettings{
ID: event.ID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
AwayTeam: event.AwayTeam,
HomeTeamID: event.HomeTeamID,
AwayTeamID: event.AwayTeamID,
HomeTeamImage: event.HomeKitImage,
AwayTeamImage: event.AwayKitImage,
LeagueID: event.LeagueID,
LeagueName: event.LeagueName,
ID: event.ID,
SourceEventID: event.SourceEventID,
WinningUpperLimit: event.WinningUpperLimit,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
AwayTeam: event.AwayTeam,
HomeTeamID: event.HomeTeamID,
AwayTeamID: event.AwayTeamID,
HomeTeamImage: event.HomeKitImage,
AwayTeamImage: event.AwayKitImage,
LeagueID: event.LeagueID,
LeagueName: event.LeagueName,
LeagueCC: ValidString{
Value: event.LeagueCc.String,
Valid: event.LeagueCc.Valid,
@ -401,8 +462,8 @@ func ConvertDBEventWithSettings(events []dbgen.EventWithSetting) []EventWithSett
return result
}
func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.SaveEventSettingsParams {
return dbgen.SaveEventSettingsParams{
func ConvertUpdateTenantEventSettings(event UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams {
return dbgen.SaveTenantEventSettingsParams{
EventID: event.EventID,
CompanyID: event.CompanyID,
IsActive: event.IsActive.ToPG(),
@ -410,10 +471,19 @@ func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.SaveEventSettin
WinningUpperLimit: event.WinningUpperLimit.ToPG(),
}
}
func ConvertUpdateGlobalEventSettings(event UpdateGlobalEventSettings) dbgen.UpdateGlobalEventSettingsParams {
return dbgen.UpdateGlobalEventSettingsParams{
ID: event.EventID,
DefaultIsActive: event.IsActive.ToPG(),
DefaultIsFeatured: event.IsFeatured.ToPG(),
DefaultWinningUpperLimit: event.WinningUpperLimit.ToPG(),
}
}
func ConvertEventRes(event BaseEvent) BaseEventRes {
return BaseEventRes{
ID: event.ID,
SourceEventID: event.SourceEventID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
@ -452,6 +522,7 @@ func ConvertEventResList(events []BaseEvent) []BaseEventRes {
func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
return EventWithSettingsRes{
ID: event.ID,
SourceEventID: event.SourceEventID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
@ -480,6 +551,7 @@ func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
MatchPeriod: event.MatchPeriod.Value,
IsLive: event.IsLive,
FetchedAt: event.FetchedAt.UTC(),
UpdatedAt: event.UpdatedAt,
}
}

View File

@ -87,6 +87,17 @@ type UpdateLeague struct {
SportID ValidInt32 `json:"sport_id" example:"1"`
}
type UpdateLeagueSettingsReq struct {
IsFeatured *bool `json:"is_featured" example:"true"`
IsActive *bool `json:"is_active" example:"true"`
}
type UpdateGlobalLeagueSettings struct {
ID int64
DefaultIsActive ValidBool
DefaultIsFeatured ValidBool
}
type LeagueFilter struct {
Query ValidString
CountryCode ValidString
@ -109,8 +120,8 @@ func ConvertCreateLeague(league CreateLeague) dbgen.InsertLeagueParams {
}
}
func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.InsertLeagueSettingsParams {
return dbgen.InsertLeagueSettingsParams{
func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.SaveLeagueSettingsParams {
return dbgen.SaveLeagueSettingsParams{
CompanyID: leagueSetting.CompanyID,
LeagueID: leagueSetting.LeagueID,
IsActive: leagueSetting.IsActive.ToPG(),
@ -149,7 +160,7 @@ func ConvertDBLeagueWithSetting(lws dbgen.GetAllLeaguesWithSettingsRow) LeagueWi
ID: lws.ID,
Name: lws.Name,
CompanyID: lws.CompanyID.Int64,
CountryCode: ValidString{
CountryCode: ValidString{
Value: lws.CountryCode.String,
Valid: lws.CountryCode.Valid,
},
@ -187,15 +198,15 @@ func ConvertUpdateLeague(updateLeague UpdateLeague) dbgen.UpdateLeagueParams {
func ConvertLeagueWithSettingRes(lws LeagueWithSettings) LeagueWithSettingsRes {
return LeagueWithSettingsRes{
ID: lws.ID,
Name: lws.Name,
CompanyID: lws.CompanyID,
CountryCode: lws.CountryCode.Value,
Bet365ID: lws.Bet365ID.Value,
IsActive: lws.IsActive,
SportID: lws.SportID,
IsFeatured: lws.IsFeatured,
UpdatedAt: lws.UpdatedAt,
ID: lws.ID,
Name: lws.Name,
CompanyID: lws.CompanyID,
CountryCode: lws.CountryCode.Value,
Bet365ID: lws.Bet365ID.Value,
IsActive: lws.IsActive,
SportID: lws.SportID,
IsFeatured: lws.IsFeatured,
UpdatedAt: lws.UpdatedAt,
DefaultIsActive: lws.DefaultIsActive,
DefaultIsFeatured: lws.DefaultIsFeatured,
}
@ -213,12 +224,12 @@ func ConvertLeagueWithSettingResList(leagues []LeagueWithSettings) []LeagueWithS
func ConvertBaseLeagueRes(league BaseLeague) BaseLeagueRes {
return BaseLeagueRes{
ID: league.ID,
Name: league.Name,
CountryCode: league.CountryCode.Value,
Bet365ID: league.Bet365ID.Value,
SportID: league.SportID,
DefaultIsActive: league.DefaultIsActive,
ID: league.ID,
Name: league.Name,
CountryCode: league.CountryCode.Value,
Bet365ID: league.Bet365ID.Value,
SportID: league.SportID,
DefaultIsActive: league.DefaultIsActive,
DefaultIsFeatured: league.DefaultIsFeatured,
}
}
@ -231,3 +242,11 @@ func ConvertBaseLeagueResList(leagues []BaseLeague) []BaseLeagueRes {
return result
}
func ConvertUpdateGlobalLeagueSetting(league UpdateGlobalLeagueSettings) dbgen.UpdateGlobalLeagueSettingsParams {
return dbgen.UpdateGlobalLeagueSettingsParams{
ID: league.ID,
IsActive: league.DefaultIsActive.ToPG(),
IsFeatured: league.DefaultIsFeatured.ToPG(),
}
}

View File

@ -61,6 +61,11 @@ type CreateOddMarketSettings struct {
CustomRawOdds []map[string]interface{}
}
type UpdateGlobalOddMarketSettings struct {
OddMarketID int64
IsActive ValidBool
}
type CustomOdd struct {
OddID int64 `json:"odd_id"`
OddValue float32 `json:"odd_value"`
@ -72,6 +77,11 @@ type CreateOddMarketSettingsReq struct {
CustomOdd []CustomOdd `json:"custom_odd,omitempty"`
}
type UpdateGlobalOddMarketSettingsReq struct {
OddMarketID int64 `json:"odd_market_id"`
IsActive *bool `json:"is_active,omitempty"`
}
type RawOddsByMarketID struct {
ID int64 `json:"id"`
MarketName string `json:"market_name"`
@ -86,6 +96,8 @@ type OddMarketFilter struct {
Offset ValidInt32
}
type OddMarketWithEventFilter struct {
Status ValidString
IsLive ValidBool
Limit ValidInt32
Offset ValidInt32
}

View File

@ -1,6 +1,7 @@
package domain
import (
"fmt"
"time"
"github.com/jackc/pgx/v5/pgtype"
@ -48,6 +49,28 @@ const (
OUTCOME_STATUS_ERROR OutcomeStatus = 5 //Half Win and Half Given Back
)
func (o OutcomeStatus) IsValid() bool {
switch o {
case OUTCOME_STATUS_PENDING,
OUTCOME_STATUS_WIN,
OUTCOME_STATUS_LOSS,
OUTCOME_STATUS_VOID,
OUTCOME_STATUS_HALF,
OUTCOME_STATUS_ERROR:
return true
default:
return false
}
}
func ParseOutcomeStatus(val int) (OutcomeStatus, error) {
o := OutcomeStatus(val)
if !o.IsValid() {
return 0, fmt.Errorf("invalid OutcomeStatus: %d", val)
}
return o, nil
}
func (o *OutcomeStatus) String() string {
switch *o {
case OUTCOME_STATUS_PENDING:
@ -72,7 +95,6 @@ type ValidOutcomeStatus struct {
Valid bool
}
func (v ValidOutcomeStatus) ToPG() pgtype.Int4 {
return pgtype.Int4{
Int32: int32(v.Value),
@ -80,7 +102,6 @@ func (v ValidOutcomeStatus) ToPG() pgtype.Int4 {
}
}
type TimeStatus int32
const (

View File

@ -123,7 +123,7 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma
Query: filter.Query.ToPG(),
CreatedBefore: filter.CreatedBefore.ToPG(),
CreatedAfter: filter.CreatedAfter.ToPG(),
});
})
var result []domain.GetBet = make([]domain.GetBet, 0, len(bets))
for _, bet := range bets {
@ -275,6 +275,36 @@ func (s *Store) SettleWinningBet(ctx context.Context, betID int64, userID int64,
return nil
}
func (s *Store) GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error) {
outcomes, err := s.queries.GetBetOutcomeViewByEventID(ctx, dbgen.GetBetOutcomeViewByEventIDParams{
EventID: eventID,
FilterStatus: filter.OutcomeStatus.ToPG(),
CompanyID: filter.CompanyID.ToPG(),
Offset: filter.Offset.ToPG(),
Limit: filter.Limit.ToPG(),
})
if err != nil {
domain.MongoDBLogger.Error("failed to get bet outcomes by event ID",
zap.Int64("event_id", eventID),
zap.Error(err),
)
return nil, 0, err
}
total, err := s.queries.TotalBetOutcomeViewByEventID(ctx, dbgen.TotalBetOutcomeViewByEventIDParams{
EventID: eventID,
FilterStatus: filter.OutcomeStatus.ToPG(),
CompanyID: filter.CompanyID.ToPG(),
})
var result []domain.BetOutcomeViewRes = make([]domain.BetOutcomeViewRes, 0, len(outcomes))
for _, outcome := range outcomes {
result = append(result, domain.ConvertDBBetOutcomesView(outcome))
}
return result, total, nil
}
func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) {
outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, dbgen.GetBetOutcomeByEventIDParams{
@ -377,6 +407,27 @@ func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int6
}
return result, nil
}
func (s *Store) UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) {
outcomes, err := s.queries.UpdateBetOutcomeStatusForOddID(ctx, dbgen.UpdateBetOutcomeStatusForOddIDParams{
OddID: oddID,
Status: int32(status),
})
if err != nil {
domain.MongoDBLogger.Error("failed to update bet outcome status for oddID",
zap.Int64("oddId", oddID),
zap.Int32("status", int32(status)),
zap.Error(err),
)
return nil, err
}
var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes))
for _, outcome := range outcomes {
result = append(result, domain.ConvertDBBetOutcomes(outcome))
}
return result, nil
}
func (s *Store) UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error {
err := s.queries.UpdateBetWithCashback(ctx, dbgen.UpdateBetWithCashbackParams{

View File

@ -17,7 +17,7 @@ func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany)
i := 1
for {
_, err := s.queries.GetCompanyIDUsingSlug(ctx, uniqueSlug)
_, err := s.queries.GetCompanyUsingSlug(ctx, uniqueSlug)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
// slug is unique
@ -78,13 +78,13 @@ func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany
return domain.ConvertDBCompanyDetails(dbCompany), nil
}
func (s *Store) GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error) {
dbCompanyID, err := s.queries.GetCompanyIDUsingSlug(ctx, slug)
func (s *Store) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) {
dbCompany, err := s.queries.GetCompanyUsingSlug(ctx, slug)
if err != nil {
return 0, err
return domain.Company{}, err
}
return dbCompanyID, nil
return domain.ConvertDBCompany(dbCompany), nil
}
func (s *Store) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) {

View File

@ -3,7 +3,6 @@ package repository
import (
"context"
"fmt"
"math"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -55,8 +54,7 @@ func (s *Store) GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]
return nil, 0, err
}
numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value))
return domain.ConvertDBEvents(events), int64(numberOfPages), nil
return domain.ConvertDBEvents(events), totalCount, nil
}
func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) {
@ -99,8 +97,6 @@ func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filt
return nil, 0, err
}
numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value))
result := make([]domain.EventWithSettings, len(events))
for i, event := range events {
@ -155,7 +151,7 @@ func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filt
}
}
return result, int64(numberOfPages), nil
return result, totalCount, nil
}
func (s *Store) GetEventByID(ctx context.Context, ID int64) (domain.BaseEvent, error) {
event, err := s.queries.GetEventByID(ctx, ID)
@ -281,10 +277,13 @@ func (s *Store) UpdateEventMonitored(ctx context.Context, eventID int64, IsMonit
})
}
func (s *Store) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error {
return s.queries.SaveEventSettings(ctx, domain.ConvertUpdateEventSettings(event))
func (s *Store) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error {
return s.queries.SaveTenantEventSettings(ctx, domain.ConvertUpdateTenantEventSettings(event))
}
func (s *Store) UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error {
return s.queries.UpdateGlobalEventSettings(ctx, domain.ConvertUpdateGlobalEventSettings(event))
}
func (s *Store) DeleteEvent(ctx context.Context, eventID int64) error {
err := s.queries.DeleteEvent(ctx, eventID)
if err != nil {

View File

@ -13,7 +13,7 @@ func (s *Store) SaveLeague(ctx context.Context, league domain.CreateLeague) erro
}
func (s *Store) SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error {
return s.queries.InsertLeagueSettings(ctx, domain.ConvertCreateLeagueSettings(leagueSettings))
return s.queries.SaveLeagueSettings(ctx, domain.ConvertCreateLeagueSettings(leagueSettings))
}
func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error) {
@ -85,3 +85,7 @@ func (s *Store) CheckLeagueSupport(ctx context.Context, leagueID int64, companyI
func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {
return s.queries.UpdateLeague(ctx, domain.ConvertUpdateLeague(league))
}
func (s *Store) UpdateGlobalLeagueSettings(ctx context.Context, league domain.UpdateGlobalLeagueSettings) error {
return s.queries.UpdateGlobalLeagueSettings(ctx, domain.ConvertUpdateGlobalLeagueSetting(league))
}

View File

@ -239,17 +239,11 @@ func (s *Store) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID
func (s *Store) GetOddsByEventID(ctx context.Context, eventID int64, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) {
odds, err := s.queries.GetOddsByEventID(ctx, dbgen.GetOddsByEventIDParams{
EventID: eventID,
Status: filter.Status.ToPG(),
IsLive: filter.IsLive.ToPG(),
Limit: filter.Limit.ToPG(),
Offset: filter.Offset.ToPG(),
IsLive: pgtype.Bool{
Bool: false,
Valid: true,
},
Status: pgtype.Text{
String: string(domain.STATUS_PENDING),
Valid: true,
},
Source: pgtype.Text{},
Source: pgtype.Text{},
})
if err != nil {
return nil, err
@ -322,3 +316,21 @@ func (s *Store) SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketS
}
return s.queries.SaveOddSettings(ctx, res)
}
func (s *Store) UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error {
return s.queries.UpdateGlobalOddsSetting(ctx, dbgen.UpdateGlobalOddsSettingParams{
ID: odd.OddMarketID,
DefaultIsActive: odd.IsActive.ToPG(),
})
}
func (s *Store) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error {
return s.queries.DeleteAllCompanyOddsSetting(ctx, companyID)
}
func (s *Store) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error {
return s.queries.DeleteCompanyOddsSettingByOddMarketID(ctx, dbgen.DeleteCompanyOddsSettingByOddMarketIDParams{
CompanyID: companyID,
OddsMarketID: oddMarketID,
})
}

View File

@ -247,7 +247,7 @@ func (s *Service) SendAdminErrorNotification(ctx context.Context, betID int64, s
}
func (s *Service) SendAdminLargeBetNotification(ctx context.Context, betID int64, totalWinnings float32, extra string, companyID int64) error {
headline := fmt.Sprintf("SYSTEM WARNING: High Risk Bet", betID, totalWinnings)
headline := "SYSTEM WARNING: High Risk Bet"
message := fmt.Sprintf("Bet #%d has been created with %v payout", betID, totalWinnings)
super_admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{

View File

@ -15,6 +15,7 @@ type BetStore interface {
GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, int64, error)
GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error)
GetBetByFastCode(ctx context.Context, fastcode string) (domain.GetBet, error)
GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error)
GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error)
GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error)
GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (int64, error)
@ -25,7 +26,7 @@ type BetStore interface {
UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)
UpdateBetOutcomeStatusByBetID(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)
UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error)
UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error)
GetBetSummary(ctx context.Context, filter domain.ReportFilter) (
totalStakes domain.Currency,
totalBets int64,

View File

@ -848,6 +848,9 @@ func (s *Service) GetBetOutcomeByBetID(ctx context.Context, UserID int64) ([]dom
return s.betStore.GetBetOutcomeByBetID(ctx, UserID)
}
func (s *Service) GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error) {
return s.betStore.GetBetOutcomeViewByEventID(ctx, eventID, filter)
}
func (s *Service) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) {
return s.betStore.GetBetOutcomeByEventID(ctx, eventID, is_filtered)
}
@ -1076,6 +1079,19 @@ func (s *Service) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID in
return outcomes, nil
}
func (s *Service) UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) {
outcomes, err := s.betStore.UpdateBetOutcomeStatusForOddId(ctx, oddID, status)
if err != nil {
s.mongoLogger.Error("failed to update bet outcome status",
zap.Int64("oddID", oddID),
zap.Error(err),
)
return nil, err
}
return outcomes, nil
}
func (s *Service) SetBetToRemoved(ctx context.Context, id int64) error {
_, err := s.betStore.UpdateBetOutcomeStatusByBetID(ctx, id, domain.OUTCOME_STATUS_VOID)
if err != nil {

View File

@ -11,7 +11,7 @@ type CompanyStore interface {
GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error)
SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error)
GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error)
GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error)
GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error)
UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error)
DeleteCompany(ctx context.Context, id int64) error

View File

@ -26,8 +26,8 @@ func (s *Service) GetAllCompanies(ctx context.Context, filter domain.CompanyFilt
func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) {
return s.companyStore.GetCompanyByID(ctx, id)
}
func (s *Service) GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error){
return s.companyStore.GetCompanyIDBySlug(ctx, slug)
func (s *Service) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) {
return s.companyStore.GetCompanyBySlug(ctx, slug)
}
func (s *Service) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) {

View File

@ -18,6 +18,7 @@ type Service interface {
UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) error
GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error)
GetEventWithSettingByID(ctx context.Context, ID int64, companyID int64) (domain.EventWithSettings, error)
UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error
UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error
UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error
GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error)
}

View File

@ -491,8 +491,11 @@ func (s *service) GetEventWithSettingByID(ctx context.Context, ID int64, company
return s.store.GetEventWithSettingByID(ctx, ID, companyID)
}
func (s *service) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error {
return s.store.UpdateEventSettings(ctx, event)
func (s *service) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error {
return s.store.UpdateTenantEventSettings(ctx, event)
}
func (s *service) UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error {
return s.store.UpdateGlobalEventSettings(ctx, event)
}
func (s *service) GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) {

View File

@ -10,7 +10,8 @@ type Service interface {
SaveLeague(ctx context.Context, league domain.CreateLeague) error
SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error
GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error)
GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error)
GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error)
CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error)
UpdateLeague(ctx context.Context, league domain.UpdateLeague) error
UpdateGlobalLeagueSettings(ctx context.Context, league domain.UpdateGlobalLeagueSettings) error
}

View File

@ -40,3 +40,7 @@ func (s *service) CheckLeagueSupport(ctx context.Context, leagueID int64, compan
func (s *service) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {
return s.store.UpdateLeague(ctx, league)
}
func (s *service) UpdateGlobalLeagueSettings(ctx context.Context, league domain.UpdateGlobalLeagueSettings) error {
return s.store.UpdateGlobalLeagueSettings(ctx, league)
}

View File

@ -17,31 +17,17 @@ type Service interface {
GetALLPrematchOdds(ctx context.Context) ([]domain.OddMarket, error)
// GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.OddMarket, error)
DeleteOddsForEvent(ctx context.Context, eventID string) error
GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error)
GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error)
GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error)
// Settings
SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error
UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error
DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error
DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error
// Odd History
InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error)
GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error)
GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error)
// Disabling Odds
InsertDisabledOdd(ctx context.Context, odd domain.CreateDisabledOdd) (domain.DisabledOdd, error)
GetAllDisabledOdds(ctx context.Context) ([]domain.DisabledOdd, error)
GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (domain.DisabledOdd, error)
GetDisabledOddByID(ctx context.Context, id int64) (domain.DisabledOdd, error)
DeleteDisabledOddsByID(ctx context.Context, id int64) error
DeleteDisabledOddsByRawOddID(ctx context.Context, id int64) error
// Custom Odds
// InsertCustomOdds(ctx context.Context, odd domain.CreateCustomOdd) (domain.CustomOdd, error)
// GetAllCustomOdds(ctx context.Context, filter domain.CustomOddFilter) ([]domain.CustomOdd, error)
// GetCustomOddByID(ctx context.Context, id int64) (domain.CustomOdd, error)
// GetCustomOddByOddID(ctx context.Context, oddId int64, companyID int64) (domain.CustomOdd, error)
// DeleteCustomOddByID(ctx context.Context, id int64) error
// DeleteCustomOddsByOddID(ctx context.Context, oddId int64, companyID int64) error
// DeleteCustomOddByEventID(ctx context.Context, eventID string) error
}

View File

@ -676,6 +676,10 @@ func (s *ServiceImpl) SaveOddsSetting(ctx context.Context, odd domain.CreateOddM
return s.store.SaveOddsSetting(ctx, odd)
}
func (s *ServiceImpl) UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error {
return s.store.UpdateGlobalOddsSetting(ctx, odd);
}
func (s *ServiceImpl) SaveOddsSettingReq(ctx context.Context, companyID int64, req domain.CreateOddMarketSettingsReq) error {
odd, err := s.GetOddsWithSettingsByID(ctx, req.OddMarketID, companyID)
@ -741,6 +745,14 @@ func (s *ServiceImpl) DeleteOddsForEvent(ctx context.Context, eventID int64) err
return s.store.DeleteOddsForEvent(ctx, eventID)
}
func (s *ServiceImpl) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error {
return s.store.DeleteAllCompanyOddsSetting(ctx, companyID)
}
func (s *ServiceImpl) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error{
return s.store.DeleteCompanyOddsSettingByOddMarketID(ctx, companyID, oddMarketID)
}
// func getString(v interface{}) string {
// if str, ok := v.(string); ok {
// return str

View File

@ -27,71 +27,71 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
spec string
task func()
}{
{
spec: "0 0 * * * *", // Every 1 hour
task: func() {
mongoLogger.Info("Began fetching upcoming events cron task")
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
mongoLogger.Error("Failed to fetch upcoming events",
zap.Error(err),
)
} else {
mongoLogger.Info("Completed fetching upcoming events without errors")
}
},
},
{
spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
task: func() {
mongoLogger.Info("Began fetching non live odds cron task")
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
mongoLogger.Error("Failed to fetch non live odds",
zap.Error(err),
)
} else {
mongoLogger.Info("Completed fetching non live odds without errors")
}
},
},
{
spec: "0 */5 * * * *", // Every 5 Minutes
task: func() {
mongoLogger.Info("Began update all expired events status cron task")
if _, err := resultService.CheckAndUpdateExpiredB365Events(context.Background()); err != nil {
mongoLogger.Error("Failed to update expired events status",
zap.Error(err),
)
} else {
mongoLogger.Info("Completed expired events without errors")
}
},
},
{
spec: "0 */15 * * * *", // Every 15 Minutes
task: func() {
mongoLogger.Info("Began updating bets based on event results cron task")
if err := resultService.FetchB365ResultAndUpdateBets(context.Background()); err != nil {
mongoLogger.Error("Failed to process result",
zap.Error(err),
)
} else {
mongoLogger.Info("Completed processing all event result outcomes without errors")
}
},
},
{
spec: "0 0 0 * * 1", // Every Monday
task: func() {
mongoLogger.Info("Began Send weekly result notification cron task")
if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-7*24*time.Hour)); err != nil {
mongoLogger.Error("Failed to process result",
zap.Error(err),
)
} else {
mongoLogger.Info("Completed sending weekly result notification without errors")
}
},
},
// {
// spec: "0 0 * * * *", // Every 1 hour
// task: func() {
// mongoLogger.Info("Began fetching upcoming events cron task")
// if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
// mongoLogger.Error("Failed to fetch upcoming events",
// zap.Error(err),
// )
// } else {
// mongoLogger.Info("Completed fetching upcoming events without errors")
// }
// },
// },
// {
// spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
// task: func() {
// mongoLogger.Info("Began fetching non live odds cron task")
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
// mongoLogger.Error("Failed to fetch non live odds",
// zap.Error(err),
// )
// } else {
// mongoLogger.Info("Completed fetching non live odds without errors")
// }
// },
// },
// {
// spec: "0 */5 * * * *", // Every 5 Minutes
// task: func() {
// mongoLogger.Info("Began update all expired events status cron task")
// if _, err := resultService.CheckAndUpdateExpiredB365Events(context.Background()); err != nil {
// mongoLogger.Error("Failed to update expired events status",
// zap.Error(err),
// )
// } else {
// mongoLogger.Info("Completed expired events without errors")
// }
// },
// },
// {
// spec: "0 */15 * * * *", // Every 15 Minutes
// task: func() {
// mongoLogger.Info("Began updating bets based on event results cron task")
// if err := resultService.FetchB365ResultAndUpdateBets(context.Background()); err != nil {
// mongoLogger.Error("Failed to process result",
// zap.Error(err),
// )
// } else {
// mongoLogger.Info("Completed processing all event result outcomes without errors")
// }
// },
// },
// {
// spec: "0 0 0 * * 1", // Every Monday
// task: func() {
// mongoLogger.Info("Began Send weekly result notification cron task")
// if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-7*24*time.Hour)); err != nil {
// mongoLogger.Error("Failed to process result",
// zap.Error(err),
// )
// } else {
// mongoLogger.Info("Completed sending weekly result notification without errors")
// }
// },
// },
}
for _, job := range schedule {

View File

@ -451,24 +451,24 @@ func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update admin:"+err.Error())
}
if req.CompanyID != nil {
_, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
ID: *req.CompanyID,
AdminID: domain.ValidInt64{
Value: AdminID,
Valid: true,
},
})
if err != nil {
h.mongoLoggerSvc.Error("UpdateAdmin failed to update company",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Int64("admin_id", AdminID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company:"+err.Error())
}
}
// if req.CompanyID != nil {
// _, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
// ID: *req.CompanyID,
// AdminID: domain.ValidInt64{
// Value: AdminID,
// Valid: true,
// },
// })
// if err != nil {
// h.mongoLoggerSvc.Error("UpdateAdmin failed to update company",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Int64("admin_id", AdminID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company:"+err.Error())
// }
// }
h.mongoLoggerSvc.Info("UpdateAdmin succeeded",
zap.Int("status_code", fiber.StatusOK),

View File

@ -169,7 +169,7 @@ type loginAdminRes struct {
func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req loginAdminReq

View File

@ -552,6 +552,25 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
Valid: true,
}
}
var companyID domain.ValidInt64
companyIDQuery := c.Query("company_id")
if companyIDQuery != "" {
companyIDParsed, err := strconv.ParseInt(companyIDQuery, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("invalid company_id format",
zap.String("company_id", companyIDQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid company_id format")
}
companyID = domain.ValidInt64{
Value: companyIDParsed,
Valid: true,
}
}
bets, total, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{
IsShopBet: isShopBet,
Query: searchString,
@ -560,6 +579,7 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
Status: statusFilter,
Limit: limit,
Offset: offset,
CompanyID: companyID,
})
if err != nil {
h.mongoLoggerSvc.Error("Failed to get all bets",

View File

@ -135,6 +135,40 @@ func (h *Handler) GetAllEvents(c *fiber.Ctx) error {
}
}
isActiveQuery := c.Query("is_active")
var isActive domain.ValidBool
if isActiveQuery != "" {
isActiveParsed, err := strconv.ParseBool(isActiveQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse isActive",
zap.String("is_active", isActiveQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_active")
}
isActive = domain.ValidBool{
Value: isActiveParsed,
Valid: true,
}
}
statusQuery := c.Query("status")
var eventStatus domain.ValidEventStatus
if statusQuery != "" {
eventStatusParsed, err := domain.ParseEventStatus(statusQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse statusQuery",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid event status string")
}
eventStatus = domain.ValidEventStatus{
Value: eventStatusParsed,
Valid: true,
}
}
events, total, err := h.eventSvc.GetAllEvents(
c.Context(), domain.EventFilter{
SportID: sportID,
@ -146,6 +180,8 @@ func (h *Handler) GetAllEvents(c *fiber.Ctx) error {
Offset: offset,
CountryCode: countryCode,
Featured: isFeatured,
Active: isActive,
Status: eventStatus,
})
// fmt.Printf("League ID: %v", leagueID)
@ -294,18 +330,18 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
events, total, err := h.eventSvc.GetEventsWithSettings(
c.Context(), companyID.Value, domain.EventFilter{
SportID: sportID,
LeagueID: leagueID,
Query: searchString,
SportID: sportID,
LeagueID: leagueID,
Query: searchString,
FirstStartTime: domain.ValidTime{
Value: time.Now(),
Valid: true,
},
LastStartTime: lastStartTime,
Limit: limit,
Offset: offset,
CountryCode: countryCode,
Featured: isFeatured,
LastStartTime: lastStartTime,
Limit: limit,
Offset: offset,
CountryCode: countryCode,
Featured: isFeatured,
Status: domain.ValidEventStatus{
Value: domain.STATUS_PENDING,
Valid: true,
@ -334,6 +370,200 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
}
// @Summary Retrieve all upcoming events with settings
// @Description Retrieve all upcoming events settings from the database
// @Tags prematch
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Param league_id query string false "League ID Filter"
// @Param sport_id query string false "Sport ID Filter"
// @Param cc query string false "Country Code Filter"
// @Param first_start_time query string false "Start Time"
// @Param last_start_time query string false "End Time"
// @Success 200 {array} domain.BaseEvent
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/events [get]
func (h *Handler) GetTenantEvents(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10)
limit := domain.ValidInt32{
Value: int32(pageSize),
Valid: true,
}
offset := domain.ValidInt32{
Value: int32(page - 1),
Valid: true,
}
leagueIDQuery := c.Query("league_id")
var leagueID domain.ValidInt64
if leagueIDQuery != "" {
leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64)
if err != nil {
h.BadRequestLogger().Error("invalid league id",
zap.String("league_id", leagueIDQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
}
leagueID = domain.ValidInt64{
Value: leagueIDInt,
Valid: true,
}
}
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil {
h.BadRequestLogger().Info("invalid sport id",
zap.String("sportID", sportIDQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid sport id")
}
sportID = domain.ValidInt32{
Value: int32(sportIDint),
Valid: true,
}
}
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
firstStartTimeQuery := c.Query("first_start_time")
var firstStartTime domain.ValidTime
if firstStartTimeQuery != "" {
firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
if err != nil {
h.BadRequestLogger().Info("invalid start_time format",
zap.String("first_start_time", firstStartTimeQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
}
firstStartTime = domain.ValidTime{
Value: firstStartTimeParsed,
Valid: true,
}
}
lastStartTimeQuery := c.Query("last_start_time")
var lastStartTime domain.ValidTime
if lastStartTimeQuery != "" {
lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery)
if err != nil {
h.BadRequestLogger().Info("invalid last_start_time format",
zap.String("last_start_time", lastStartTimeQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
}
lastStartTime = domain.ValidTime{
Value: lastStartTimeParsed,
Valid: true,
}
}
countryCodeQuery := c.Query("cc")
countryCode := domain.ValidString{
Value: countryCodeQuery,
Valid: countryCodeQuery != "",
}
isFeaturedQuery := c.Query("is_featured")
var isFeatured domain.ValidBool
if isFeaturedQuery != "" {
isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse isFeatured",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_featured")
}
isFeatured = domain.ValidBool{
Value: isFeaturedParsed,
Valid: true,
}
}
isActiveQuery := c.Query("is_active")
var isActive domain.ValidBool
if isActiveQuery != "" {
isActiveParsed, err := strconv.ParseBool(isActiveQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse isActive",
zap.String("is_active", isActiveQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_active")
}
isActive = domain.ValidBool{
Value: isActiveParsed,
Valid: true,
}
}
statusQuery := c.Query("status")
var eventStatus domain.ValidEventStatus
if statusQuery != "" {
eventStatusParsed, err := domain.ParseEventStatus(statusQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse statusQuery",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid event status string")
}
eventStatus = domain.ValidEventStatus{
Value: eventStatusParsed,
Valid: true,
}
}
events, total, err := h.eventSvc.GetEventsWithSettings(
c.Context(), companyID.Value, domain.EventFilter{
SportID: sportID,
LeagueID: leagueID,
Query: searchString,
FirstStartTime: firstStartTime,
LastStartTime: lastStartTime,
Limit: limit,
Offset: offset,
CountryCode: countryCode,
Featured: isFeatured,
Status: eventStatus,
Active: isActive,
})
// fmt.Printf("League ID: %v", leagueID)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to retrieve all upcoming events",
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
res := domain.ConvertEventWithSettingResList(events)
return response.WritePaginatedJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", res, nil, page, int(total))
}
type TopLeaguesRes struct {
Leagues []TopLeague `json:"leagues"`
}
@ -483,6 +713,145 @@ func (h *Handler) GetTenantEventByID(c *fiber.Ctx) error {
}
// @Summary Retrieve bet outcomes by event id
// @Description Retrieve bet outcomes by event id
// @Tags prematch
// @Accept json
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} domain.BaseEvent
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/tenant/{tenant_slug}/events/{id}/bets [get]
func (h *Handler) GetTenantBetsByEventID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
idStr := c.Params("id")
eventID, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10)
limit := domain.ValidInt32{
Value: int32(pageSize),
Valid: true,
}
offset := domain.ValidInt32{
Value: int32(page - 1),
Valid: true,
}
statusQuery := c.Params("status")
var status domain.ValidOutcomeStatus
if statusQuery != "" {
statusIntParse, err := strconv.ParseInt(statusQuery, 10, 32)
if err != nil {
h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery))
return fiber.NewError(fiber.StatusBadRequest, "Invalid status query")
}
statusParsed, err := domain.ParseOutcomeStatus(int(statusIntParse))
if err != nil {
h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery))
return fiber.NewError(fiber.StatusBadRequest, "Invalid status query")
}
status = domain.ValidOutcomeStatus{
Value: statusParsed,
Valid: true,
}
}
res, total, err := h.betSvc.GetBetOutcomeViewByEventID(c.Context(), eventID, domain.BetOutcomeViewFilter{
OutcomeStatus: status,
CompanyID: companyID,
Limit: limit,
Offset: offset,
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to get upcoming event by id",
zap.Int64("eventID", eventID),
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WritePaginatedJSON(c, fiber.StatusOK, "Bet Outcomes retrieved successfully", res, nil, page, int(total))
}
// @Summary Retrieve bet outcomes by event id
// @Description Retrieve bet outcomes by event id
// @Tags prematch
// @Accept json
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} domain.BaseEvent
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/events/{id}/bets [get]
func (h *Handler) GetBetsByEventID(c *fiber.Ctx) error {
idStr := c.Params("id")
eventID, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10)
limit := domain.ValidInt32{
Value: int32(pageSize),
Valid: true,
}
offset := domain.ValidInt32{
Value: int32(page - 1),
Valid: true,
}
statusQuery := c.Params("status")
var status domain.ValidOutcomeStatus
if statusQuery != "" {
statusIntParse, err := strconv.ParseInt(statusQuery, 10, 32)
if err != nil {
h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery))
return fiber.NewError(fiber.StatusBadRequest, "Invalid status query")
}
statusParsed, err := domain.ParseOutcomeStatus(int(statusIntParse))
if err != nil {
h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery))
return fiber.NewError(fiber.StatusBadRequest, "Invalid status query")
}
status = domain.ValidOutcomeStatus{
Value: statusParsed,
Valid: true,
}
}
res, total, err := h.betSvc.GetBetOutcomeViewByEventID(c.Context(), eventID, domain.BetOutcomeViewFilter{
OutcomeStatus: status,
Limit: limit,
Offset: offset,
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to get upcoming event by id",
zap.Int64("eventID", eventID),
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WritePaginatedJSON(c, fiber.StatusOK, "Bet Outcomes retrieved successfully", res, nil, page, int(total))
}
type UpdateEventStatusReq struct {
}
@ -519,9 +888,9 @@ func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error {
}
type UpdateEventSettingsReq struct {
Featured *bool `json:"is_featured" example:"true"`
IsActive *bool `json:"is_active" example:"true"`
WinningUpperLimit *int `json:"winning_upper_limit" example:"10000"`
Featured *bool `json:"is_featured" example:"true"`
IsActive *bool `json:"is_active" example:"true"`
WinningUpperLimit *int64 `json:"winning_upper_limit" example:"10000"`
}
// UpdateEventSettings godoc
@ -534,8 +903,72 @@ type UpdateEventSettingsReq struct {
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/tenant/{tenant_slug}/events/{id}/settings [put]
// @Router /api/v1/events/{id}/settings [put]
func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
eventIDStr := c.Params("id")
eventID, err := strconv.ParseInt(eventIDStr, 10, 64)
if err != nil {
h.BadRequestLogger().Error("invalid event id")
return fiber.NewError(fiber.StatusBadRequest, "invalid event id")
}
var req UpdateEventSettingsReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("Failed to parse event id",
zap.Int64("eventID", eventID),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
logFields := []zap.Field{
zap.Int64("eventID", eventID),
zap.Any("is_featured", req.Featured),
zap.Any("is_active", req.IsActive),
zap.Any("winning_upper_limit", req.WinningUpperLimit),
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Error("Failed to update event settings",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err = h.eventSvc.UpdateGlobalEventSettings(c.Context(), domain.UpdateGlobalEventSettings{
EventID: eventID,
IsFeatured: domain.ConvertBoolPtr(req.Featured),
IsActive: domain.ConvertBoolPtr(req.IsActive),
WinningUpperLimit: domain.ConvertInt64Ptr(req.WinningUpperLimit),
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update event settings", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil)
}
// UpdateTenantEventSettings godoc
// @Summary update the event settings
// @Description Update the event settings
// @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 /api/v1/tenant/{tenant_slug}/events/{id}/settings [put]
func (h *Handler) UpdateTenantEventSettings(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
@ -579,12 +1012,12 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err = h.eventSvc.UpdateEventSettings(c.Context(), domain.CreateEventSettings{
err = h.eventSvc.UpdateTenantEventSettings(c.Context(), domain.UpdateTenantEventSettings{
CompanyID: companyID.Value,
EventID: eventID,
IsFeatured: domain.ConvertBoolPtr(req.Featured),
IsActive: domain.ConvertBoolPtr(req.IsActive),
WinningUpperLimit: domain.ConvertIntPtr(req.WinningUpperLimit),
WinningUpperLimit: domain.ConvertInt64Ptr(req.WinningUpperLimit),
})
if err != nil {

View File

@ -26,7 +26,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
limit := domain.ValidInt64{
Value: int64(pageSize),
Valid: pageSize == 0,
Valid: pageSize != 0,
}
offset := domain.ValidInt64{
Value: int64(page - 1),
@ -54,7 +54,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
sportIDint, err := strconv.ParseInt(sportIDQuery, 10, 64)
if err != nil {
h.BadRequestLogger().Info("invalid sport id",
zap.String("sport_id", sportIDQuery),
@ -156,7 +156,7 @@ func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
sportIDint, err := strconv.ParseInt(sportIDQuery, 10, 64)
if err != nil {
h.BadRequestLogger().Info("invalid sport id",
zap.String("sport_id", sportIDQuery),
@ -235,7 +235,7 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
}
leagueId, err := strconv.Atoi(leagueIdStr)
leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
}
@ -266,7 +266,7 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
}
if err := h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{
LeagueID: int64(leagueId),
LeagueID: leagueId,
CompanyID: companyID.Value,
IsActive: domain.ValidBool{
Value: req.IsActive,
@ -308,7 +308,7 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
}
leagueId, err := strconv.Atoi(leagueIdStr)
leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
}
@ -336,7 +336,7 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
}
err = h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{
LeagueID: int64(leagueId),
LeagueID: leagueId,
CompanyID: companyID.Value,
IsFeatured: domain.ValidBool{
Value: req.IsFeatured,
@ -351,3 +351,45 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
h.SuccessResLogger().Info("League Featured has been successfully updated", queryLogFields...)
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
}
func (h *Handler) UpdateGlobalLeagueSetting(c *fiber.Ctx) error {
leagueIdStr := c.Params("id")
if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
}
leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
}
var req domain.UpdateLeagueSettingsReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("UpdateLeagueSettingsReq failed to parse request body", zap.String("league_id", leagueIdStr), zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request body:"+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)
}
h.BadRequestLogger().Info("Failed to validate UpdateLeagueSettingsReq", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err = h.leagueSvc.UpdateGlobalLeagueSettings(c.Context(), domain.UpdateGlobalLeagueSettings{
ID: leagueId,
DefaultIsActive: domain.ConvertBoolPtr(req.IsActive),
DefaultIsFeatured: domain.ConvertBoolPtr(req.IsFeatured),
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update league", zap.Error(err), zap.String("leagueId", leagueIdStr))
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
}

View File

@ -276,7 +276,6 @@ func (h *Handler) GetTenantOddsByUpcomingID(c *fiber.Ctx) error {
zap.Int64("company_id", companyID.Value),
}
eventIDStr := c.Params("upcoming_id")
eventID, err := strconv.ParseInt(eventIDStr, 10, 64)
if err != nil {
@ -309,16 +308,16 @@ func (h *Handler) GetTenantOddsByUpcomingID(c *fiber.Ctx) error {
}
func (h *Handler) SaveOddsSetting(c *fiber.Ctx) error {
func (h *Handler) SaveTenantOddsSetting(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req domain.CreateOddMarketSettingsReq
var req domain.CreateOddMarketSettingsReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("Failed to parse event id",
h.BadRequestLogger().Info("Failed to parse CreateOddMarketSettingsReq",
zap.Int64("CompanyID", companyID.Value),
zap.Error(err),
)
@ -337,7 +336,7 @@ func (h *Handler) SaveOddsSetting(c *fiber.Ctx) error {
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Error("Failed to insert odd settings",
h.BadRequestLogger().Error("Failed to validate insert odd settings",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
@ -354,3 +353,133 @@ func (h *Handler) SaveOddsSetting(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Odds Settings saved successfully", nil, nil)
}
func (h *Handler) SaveOddSettings(c *fiber.Ctx) error {
var req domain.UpdateGlobalOddMarketSettingsReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("Failed to parse CreateOddMarketSettingsReq",
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
logFields := []zap.Field{
zap.Any("is_active", req.IsActive),
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Error("Failed to validate insert odd settings",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err := h.prematchSvc.UpdateGlobalOddsSetting(c.Context(), domain.UpdateGlobalOddMarketSettings{
OddMarketID: req.OddMarketID,
IsActive: domain.ConvertBoolPtr(req.IsActive),
})
if err != nil {
logFields = append(logFields, zap.Error(err))
h.InternalServerErrorLogger().Error("Failed to save odds settings", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to save odds settings"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Odds Settings saved successfully", nil, nil)
}
func (h *Handler) RemoveOddsSettings(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
idStr := c.Params("id")
oddID, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
h.BadRequestLogger().Info("Failed to parse odd id", zap.String("id", idStr))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
err = h.prematchSvc.DeleteCompanyOddsSettingByOddMarketID(c.Context(), companyID.Value, oddID)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to retrieve odds", zap.Error(err))
return fiber.NewError(fiber.StatusInternalServerError, "Failed to remove odds settings"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Odds settings successfully removed ", nil, nil)
}
func (h *Handler) RemoveAllOddsSettings(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
err := h.prematchSvc.DeleteAllCompanyOddsSetting(c.Context(), companyID.Value)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to remove all odd settings", zap.Int64("company_id", companyID.Value), zap.Error(err))
return fiber.NewError(fiber.StatusInternalServerError, "Failed to remove all odds settings"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Odds settings successfully removed ", nil, nil)
}
type UpdateAllBetStatusByOddIDReq struct {
Status domain.OutcomeStatus `json:"status"`
}
func (h *Handler) UpdateAllBetOutcomeStatusByOddID(c *fiber.Ctx) error {
idStr := c.Params("id")
oddID, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
h.BadRequestLogger().Info("Failed to parse odd_id", zap.String("id", idStr))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
var req UpdateAllBetStatusByOddIDReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("Failed to parse event id",
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
logFields := []zap.Field{
zap.Any("status", req.Status),
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Error("Failed to insert odd settings",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
_, err = h.betSvc.UpdateBetOutcomeStatusForOddId(c.Context(), oddID, req.Status)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update bet status by odd id",
zap.Int64("oddID", oddID),
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Updated All Bet Outcome Status Successfully", nil, nil)
}

View File

@ -14,6 +14,74 @@ import (
"go.uber.org/zap"
)
type GetTenantSlugByToken struct {
Slug string `json:"slug"`
}
// GetTenantSlugByToken godoc
// @Summary Check if phone number or email exist
// @Description Check if phone number or email exist
// @Tags user
// @Accept json
// @Produce json
// @Param checkPhoneEmailExist body CheckPhoneEmailExistReq true "Check phone number or email exist"
// @Success 200 {object} CheckPhoneEmailExistRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/tenant [get]
func (h *Handler) GetTenantSlugByToken(c *fiber.Ctx) error {
userID, ok := c.Locals("user_id").(int64)
if !ok || userID == 0 {
h.mongoLoggerSvc.Error("Invalid user ID in context",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification")
}
user, err := h.userSvc.GetUserByID(c.Context(), userID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get user profile",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile:"+err.Error())
}
if !user.CompanyID.Valid {
if user.Role == domain.RoleSuperAdmin {
return fiber.NewError(fiber.StatusBadRequest, "Role Super-Admin Doesn't have a company-id")
}
h.mongoLoggerSvc.Error("Unknown Error: User doesn't have a company-id",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Unknown Error: User doesn't have a company-id")
}
company, err := h.companySvc.GetCompanyByID(c.Context(), user.CompanyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get company by id",
zap.Int64("company", user.CompanyID.Value),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve company:"+err.Error())
}
res := GetTenantSlugByToken{
Slug: company.Slug,
}
return response.WriteJSON(c, fiber.StatusOK, "Tenant Slug retrieved successfully", res, nil)
}
type CheckPhoneEmailExistReq struct {
Email string `json:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" example:"1234567890"`

View File

@ -236,7 +236,7 @@ func (a *App) TenantMiddleware(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "tenant is required for this route")
}
companyID, err := a.companySvc.GetCompanyIDBySlug(c.Context(), tenantSlug)
company, err := a.companySvc.GetCompanyBySlug(c.Context(), tenantSlug)
if err != nil {
a.mongoLoggerSvc.Info("failed to resolve tenant",
zap.String("tenant_slug", tenantSlug),
@ -245,8 +245,16 @@ func (a *App) TenantMiddleware(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "failed to resolve tenant")
}
if !company.IsActive {
a.mongoLoggerSvc.Info("request using deactivated tenant",
zap.String("tenant_slug", tenantSlug),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusForbidden, "this tenant has been deactivated")
}
c.Locals("company_id", domain.ValidInt64{
Value: companyID,
Value: company.ID,
Valid: true,
})
return c.Next()

View File

@ -96,6 +96,10 @@ func (a *App) initAppRoutes() {
"message": "Company Tenant Active",
})
})
// Get S
groupV1.Get("/tenant", a.authMiddleware, h.GetTenantSlugByToken)
//Direct_deposit
groupV1.Post("/direct_deposit", a.authMiddleware, h.InitiateDirectDeposit)
groupV1.Post("/direct_deposit/verify", a.authMiddleware, h.VerifyDirectDeposit)
@ -219,55 +223,63 @@ func (a *App) initAppRoutes() {
// groupV1.Post("/bonus/create", a.authMiddleware, h.CreateBonusMultiplier)
// groupV1.Put("/bonus/update", a.authMiddleware, h.UpdateBonusMultiplier)
groupV1.Get("/cashiers", a.authMiddleware, h.GetAllCashiers)
groupV1.Get("/cashiers/:id", a.authMiddleware, h.GetCashierByID)
groupV1.Post("/cashiers", a.authMiddleware, h.CreateCashier)
groupV1.Put("/cashiers/:id", a.authMiddleware, h.UpdateCashier)
groupV1.Get("/cashiers", a.authMiddleware, a.CompanyOnly, h.GetAllCashiers)
groupV1.Get("/cashiers/:id", a.authMiddleware, a.CompanyOnly, h.GetCashierByID)
groupV1.Post("/cashiers", a.authMiddleware, a.CompanyOnly, h.CreateCashier)
groupV1.Put("/cashiers/:id", a.authMiddleware, a.CompanyOnly, h.UpdateCashier)
tenant.Get("/customer", a.authMiddleware, h.GetAllTenantCustomers)
tenant.Get("/customer/:id", a.authMiddleware, h.GetTenantCustomerByID)
tenant.Put("/customer/:id", a.authMiddleware, h.UpdateTenantCustomer)
tenant.Get("/customer/:id/bets", a.authMiddleware, h.GetTenantCustomerBets)
tenant.Get("/customer", a.authMiddleware, a.CompanyOnly, h.GetAllTenantCustomers)
tenant.Get("/customer/:id", a.authMiddleware, a.CompanyOnly, h.GetTenantCustomerByID)
tenant.Put("/customer/:id", a.authMiddleware, a.CompanyOnly, h.UpdateTenantCustomer)
tenant.Get("/customer/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetTenantCustomerBets)
groupV1.Get("/customer", a.authMiddleware, a.SuperAdminOnly, h.GetAllCustomers)
groupV1.Get("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCustomerByID)
groupV1.Put("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCustomer)
groupV1.Get("/customer/:id/bets", a.authMiddleware, h.GetCustomerBets)
groupV1.Get("/admin", a.authMiddleware, h.GetAllAdmins)
groupV1.Get("/admin/:id", a.authMiddleware, h.GetAdminByID)
groupV1.Post("/admin", a.authMiddleware, h.CreateAdmin)
groupV1.Put("/admin/:id", a.authMiddleware, h.UpdateAdmin)
groupV1.Get("/admin", a.authMiddleware, a.SuperAdminOnly, h.GetAllAdmins)
groupV1.Get("/admin/:id", a.authMiddleware, a.SuperAdminOnly, h.GetAdminByID)
groupV1.Post("/admin", a.authMiddleware, a.SuperAdminOnly, h.CreateAdmin)
groupV1.Put("/admin/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateAdmin)
groupV1.Get("/t-approver", a.authMiddleware, h.GetAllTransactionApprovers)
groupV1.Get("/t-approver/:id", a.authMiddleware, h.GetTransactionApproverByID)
groupV1.Post("/t-approver", a.authMiddleware, h.CreateTransactionApprover)
groupV1.Put("/t-approver/:id", a.authMiddleware, h.UpdateTransactionApprover)
groupV1.Get("/t-approver", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllTransactionApprovers)
groupV1.Get("/t-approver/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetTransactionApproverByID)
groupV1.Post("/t-approver", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateTransactionApprover)
groupV1.Put("/t-approver/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateTransactionApprover)
groupV1.Get("/managers", a.authMiddleware, h.GetAllManagers)
groupV1.Get("/managers", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllManagers)
groupV1.Get("/managers/:id", a.authMiddleware, h.GetManagerByID)
groupV1.Post("/managers", a.authMiddleware, h.CreateManager)
groupV1.Put("/managers/:id", a.authMiddleware, h.UpdateManagers)
groupV1.Get("/manager/:id/branch", a.authMiddleware, h.GetBranchByManagerID)
groupV1.Post("/managers", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateManager)
groupV1.Put("/managers/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateManagers)
groupV1.Get("/manager/:id/branch", a.authMiddleware, a.OnlyAdminAndAbove, h.GetBranchByManagerID)
groupV1.Get("/odds", a.authMiddleware, a.SuperAdminOnly, h.GetAllOdds)
groupV1.Get("/odds/upcoming/:upcoming_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByUpcomingID)
groupV1.Get("/odds/upcoming/:upcoming_id/market/:market_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByMarketID)
groupV1.Post("/odds/settings", a.SuperAdminOnly, h.SaveOddSettings)
groupV1.Put("/odds/bet-outcome/:id", a.SuperAdminOnly, h.UpdateAllBetOutcomeStatusByOddID)
tenant.Get("/odds", h.GetAllTenantOdds)
tenant.Get("/odds/upcoming/:upcoming_id", h.GetTenantOddsByUpcomingID)
tenant.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetTenantOddsByMarketID)
tenant.Post("/odds/settings", a.CompanyOnly, h.SaveOddsSetting)
tenant.Post("/odds/settings", a.CompanyOnly, h.SaveTenantOddsSetting)
tenant.Delete("/odds/settings/:id", a.CompanyOnly, h.RemoveOddsSettings)
tenant.Delete("/odds/settings", a.CompanyOnly, h.RemoveAllOddsSettings)
groupV1.Get("/events", a.authMiddleware, h.GetAllEvents)
groupV1.Get("/events/:id", a.authMiddleware, h.GetEventByID)
groupV1.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved)
groupV1.Patch("/events/:id/is_monitored", a.authMiddleware, a.SuperAdminOnly, h.SetEventIsMonitored)
groupV1.Put("/events/:id/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalSettingList)
groupV1.Get("/events/:id/bets", a.authMiddleware, a.SuperAdminOnly, h.GetBetsByEventID)
tenant.Get("/upcoming-events", h.GetTenantUpcomingEvents)
tenant.Get("/events/:id", h.GetTenantEventByID)
tenant.Get("/top-leagues", h.GetTopLeagues)
tenant.Put("/events/:id/settings", h.UpdateEventSettings)
tenant.Get("/events", h.GetTenantEvents)
tenant.Get("/events/:id", h.GetTenantEventByID)
tenant.Put("/events/:id/settings", a.authMiddleware, a.CompanyOnly, h.UpdateTenantEventSettings)
tenant.Get("/events/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetTenantBetsByEventID)
//EnetPulse
groupV1.Get("/odds/pre-match", h.GetPreMatchOdds)
@ -277,38 +289,40 @@ func (a *App) initAppRoutes() {
groupV1.Get("/tournament_stages", h.GetAllTournamentStages)
// Leagues
tenant.Get("/leagues", h.GetAllLeagues)
tenant.Put("/leagues/:id/set-active", h.SetLeagueActive)
tenant.Put("/leagues/:id/featured", h.SetLeagueFeatured)
groupV1.Get("/leagues", a.authMiddleware, a.SuperAdminOnly, h.GetAllLeagues)
groupV1.Put("/leagues/:id/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalLeagueSetting)
tenant.Get("/leagues", h.GetAllLeagues)
tenant.Put("/leagues/:id/featured", a.authMiddleware, a.CompanyOnly, h.SetLeagueFeatured)
tenant.Put("/leagues/:id/set-active", a.authMiddleware, a.CompanyOnly, h.SetLeagueActive)
groupV1.Get("/result/b365/:id", h.GetBet365ResultsByEventID)
// Branch
groupV1.Post("/branch", a.authMiddleware, h.CreateBranch)
groupV1.Get("/branch", a.authMiddleware, h.GetAllBranches)
groupV1.Get("/branch/:id", a.authMiddleware, h.GetBranchByID)
groupV1.Post("/branch/:id/return", a.authMiddleware, h.ReturnBranchWallet)
groupV1.Get("/branch/:id/bets", a.authMiddleware, h.GetBetByBranchID)
groupV1.Put("/branch/:id", a.authMiddleware, h.UpdateBranch)
groupV1.Put("/branch/:id/set-active", a.authMiddleware, h.UpdateBranchStatus)
groupV1.Put("/branch/:id/set-inactive", a.authMiddleware, h.UpdateBranchStatus)
groupV1.Delete("/branch/:id", a.authMiddleware, h.DeleteBranch)
groupV1.Post("/branch", a.authMiddleware, a.CompanyOnly, h.CreateBranch)
groupV1.Get("/branch", a.authMiddleware, a.CompanyOnly, h.GetAllBranches)
groupV1.Get("/branch/:id", a.authMiddleware, a.CompanyOnly, h.GetBranchByID)
groupV1.Post("/branch/:id/return", a.authMiddleware, a.CompanyOnly, h.ReturnBranchWallet)
groupV1.Get("/branch/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetBetByBranchID)
groupV1.Put("/branch/:id", a.authMiddleware, a.CompanyOnly, h.UpdateBranch)
groupV1.Put("/branch/:id/set-active", a.authMiddleware, a.CompanyOnly, h.UpdateBranchStatus)
groupV1.Put("/branch/:id/set-inactive", a.authMiddleware, a.CompanyOnly, h.UpdateBranchStatus)
groupV1.Delete("/branch/:id", a.authMiddleware, a.CompanyOnly, h.DeleteBranch)
groupV1.Get("/search/branch", a.authMiddleware, h.SearchBranch)
groupV1.Get("/search/branch", a.authMiddleware, a.CompanyOnly, h.SearchBranch)
groupV1.Get("/branchLocation", a.authMiddleware, h.GetAllBranchLocations)
groupV1.Get("/branchLocation", a.authMiddleware, a.CompanyOnly, h.GetAllBranchLocations)
groupV1.Get("/branch/:id/cashiers", a.authMiddleware, h.GetBranchCashiers)
groupV1.Get("/branchCashier", a.authMiddleware, h.GetBranchForCashier)
groupV1.Get("/branch/:id/cashiers", a.authMiddleware, a.CompanyOnly, h.GetBranchCashiers)
groupV1.Get("/branchCashier", a.authMiddleware, a.CompanyOnly, h.GetBranchForCashier)
// Branch Operation
groupV1.Get("/supportedOperation", a.authMiddleware, a.SuperAdminOnly, h.GetAllSupportedOperations)
groupV1.Post("/supportedOperation", a.authMiddleware, a.SuperAdminOnly, h.CreateSupportedOperation)
groupV1.Post("/operation", a.authMiddleware, h.CreateBranchOperation)
groupV1.Get("/branch/:id/operation", a.authMiddleware, h.GetBranchOperations)
groupV1.Post("/operation", a.authMiddleware, a.CompanyOnly, h.CreateBranchOperation)
groupV1.Get("/branch/:id/operation", a.authMiddleware, a.CompanyOnly, h.GetBranchOperations)
groupV1.Delete("/branch/:id/operation/:opID", a.authMiddleware, h.DeleteBranchOperation)
groupV1.Delete("/branch/:id/operation/:opID", a.authMiddleware, a.CompanyOnly, h.DeleteBranchOperation)
// Company
groupV1.Post("/company", a.authMiddleware, a.SuperAdminOnly, h.CreateCompany)
@ -316,9 +330,9 @@ func (a *App) initAppRoutes() {
groupV1.Get("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCompanyByID)
groupV1.Put("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCompany)
groupV1.Delete("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteCompany)
groupV1.Get("/company/:id/branch", a.authMiddleware, h.GetBranchByCompanyID)
groupV1.Get("/search/company", a.authMiddleware, h.SearchCompany)
groupV1.Get("/admin-company", a.authMiddleware, h.GetCompanyForAdmin)
groupV1.Get("/company/:id/branch", a.authMiddleware, a.CompanyOnly, h.GetBranchByCompanyID)
groupV1.Get("/search/company", a.authMiddleware, a.CompanyOnly, h.SearchCompany)
groupV1.Get("/admin-company", a.authMiddleware, a.CompanyOnly, h.GetCompanyForAdmin)
// Ticket Routes
tenant.Post("/ticket", h.CreateTicket)
@ -329,7 +343,7 @@ func (a *App) initAppRoutes() {
tenant.Post("/sport/bet", a.authMiddleware, h.CreateBet)
tenant.Post("/sport/bet/fastcode", a.authMiddleware, h.CreateBetWithFastCode)
tenant.Get("/sport/bet/fastcode/:fast_code", h.GetBetByFastCode)
tenant.Get("/sport/bet", a.authMiddleware, h.GetAllTenantBets)
tenant.Get("/sport/bet", a.authMiddleware, a.CompanyOnly, h.GetAllTenantBets)
tenant.Get("/sport/bet/:id", a.authMiddleware, h.GetTenantBetByID)
tenant.Patch("/sport/bet/:id", a.authMiddleware, h.UpdateCashOut)
tenant.Delete("/sport/bet/:id", a.authMiddleware, h.DeleteTenantBet)
@ -458,9 +472,9 @@ func (a *App) initAppRoutes() {
groupV1.Get("/settings/:key", a.authMiddleware, a.SuperAdminOnly, h.GetGlobalSettingByKey)
groupV1.Put("/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalSettingList)
tenant.Get("/settings", a.authMiddleware, h.GetCompanySettingList)
tenant.Put("/settings", a.authMiddleware, h.SaveCompanySettingList)
tenant.Delete("/settings/:key", a.authMiddleware, h.DeleteCompanySetting)
tenant.Delete("/settings", a.authMiddleware, h.DeleteAllCompanySetting)
tenant.Get("/settings", a.authMiddleware, a.OnlyAdminAndAbove, h.GetCompanySettingList)
tenant.Put("/settings", a.authMiddleware, a.OnlyAdminAndAbove, h.SaveCompanySettingList)
tenant.Delete("/settings/:key", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteCompanySetting)
tenant.Delete("/settings", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteAllCompanySetting)
}