fix: fixed odd and issues while integrating

This commit is contained in:
Samuel Tariku 2025-06-11 23:01:30 +03:00
parent fbe2dfd5a3
commit edec72dfcd
25 changed files with 512 additions and 278 deletions

View File

@ -89,7 +89,7 @@ func main() {
companySvc := company.NewService(store) companySvc := company.NewService(store)
leagueSvc := league.New(store) leagueSvc := league.New(store)
betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, logger) betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, logger)
resultSvc := result.NewService(store, cfg, logger, *betSvc, *oddsSvc, eventSvc) resultSvc := result.NewService(store, cfg, logger, *betSvc, *oddsSvc, eventSvc, leagueSvc)
notificationRepo := repository.NewNotificationRepository(store) notificationRepo := repository.NewNotificationRepository(store)
referalRepo := repository.NewReferralRepository(store) referalRepo := repository.NewReferralRepository(store)
vitualGameRepo := repository.NewVirtualGameRepository(store) vitualGameRepo := repository.NewVirtualGameRepository(store)

View File

@ -239,6 +239,7 @@ CREATE TABLE leagues (
name TEXT NOT NULL, name TEXT NOT NULL,
country_code TEXT, country_code TEXT,
bet365_id INT, bet365_id INT,
sport_id INT NOT NULL,
is_active BOOLEAN DEFAULT true is_active BOOLEAN DEFAULT true
); );
CREATE TABLE teams ( CREATE TABLE teams (

View File

@ -149,24 +149,25 @@ WHERE start_time > now()
AND status = 'upcoming' AND status = 'upcoming'
ORDER BY start_time ASC; ORDER BY start_time ASC;
-- name: GetExpiredUpcomingEvents :many -- name: GetExpiredUpcomingEvents :many
SELECT id, SELECT events.id,
sport_id, events.sport_id,
match_name, events.match_name,
home_team, events.home_team,
away_team, events.away_team,
home_team_id, events.home_team_id,
away_team_id, events.away_team_id,
home_kit_image, events.home_kit_image,
away_kit_image, events.away_kit_image,
league_id, events.league_id,
league_name, events.league_name,
league_cc, events.start_time,
start_time, events.is_live,
is_live, events.status,
status, events.source,
source, events.fetched_at,
fetched_at leagues.country_code as league_cc
FROM events FROM events
LEFT JOIN leagues ON leagues.id = league_id
WHERE start_time < now() WHERE start_time < now()
and ( and (
status = sqlc.narg('status') status = sqlc.narg('status')
@ -176,6 +177,7 @@ ORDER BY start_time ASC;
-- name: GetTotalEvents :one -- name: GetTotalEvents :one
SELECT COUNT(*) SELECT COUNT(*)
FROM events FROM events
LEFT JOIN leagues ON leagues.id = league_id
WHERE is_live = false WHERE is_live = false
AND status = 'upcoming' AND status = 'upcoming'
AND ( AND (
@ -183,7 +185,7 @@ WHERE is_live = false
OR sqlc.narg('league_id') IS NULL OR sqlc.narg('league_id') IS NULL
) )
AND ( AND (
sport_id = sqlc.narg('sport_id') events.sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL OR sqlc.narg('sport_id') IS NULL
) )
AND ( AND (
@ -193,26 +195,31 @@ WHERE is_live = false
AND ( AND (
start_time > sqlc.narg('first_start_time') start_time > sqlc.narg('first_start_time')
OR sqlc.narg('first_start_time') IS NULL OR sqlc.narg('first_start_time') IS NULL
)
AND (
leagues.country_code = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL
); );
-- name: GetPaginatedUpcomingEvents :many -- name: GetPaginatedUpcomingEvents :many
SELECT id, SELECT events.id,
sport_id, events.sport_id,
match_name, events.match_name,
home_team, events.home_team,
away_team, events.away_team,
home_team_id, events.home_team_id,
away_team_id, events.away_team_id,
home_kit_image, events.home_kit_image,
away_kit_image, events.away_kit_image,
league_id, events.league_id,
league_name, events.league_name,
league_cc, events.start_time,
start_time, events.is_live,
is_live, events.status,
status, events.source,
source, events.fetched_at,
fetched_at leagues.country_code as league_cc
FROM events FROM events
LEFT JOIN leagues ON leagues.id = league_id
WHERE start_time > now() WHERE start_time > now()
AND is_live = false AND is_live = false
AND status = 'upcoming' AND status = 'upcoming'
@ -221,7 +228,7 @@ WHERE start_time > now()
OR sqlc.narg('league_id') IS NULL OR sqlc.narg('league_id') IS NULL
) )
AND ( AND (
sport_id = sqlc.narg('sport_id') events.sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL OR sqlc.narg('sport_id') IS NULL
) )
AND ( AND (
@ -232,6 +239,10 @@ WHERE start_time > now()
start_time > sqlc.narg('first_start_time') start_time > sqlc.narg('first_start_time')
OR sqlc.narg('first_start_time') IS NULL OR sqlc.narg('first_start_time') IS NULL
) )
AND (
leagues.country_code = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL
)
ORDER BY start_time ASC ORDER BY start_time ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetUpcomingByID :one -- name: GetUpcomingByID :one

View File

@ -4,29 +4,37 @@ INSERT INTO leagues (
name, name,
country_code, country_code,
bet365_id, bet365_id,
sport_id,
is_active is_active
) )
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (id) DO VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO
UPDATE UPDATE
SET name = EXCLUDED.name, SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code, country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id, bet365_id = EXCLUDED.bet365_id,
is_active = EXCLUDED.is_active; is_active = EXCLUDED.is_active,
-- name: GetSupportedLeagues :many sport_id = EXCLUDED.sport_id;
SELECT id,
name,
country_code,
bet365_id,
is_active
FROM leagues
WHERE is_active = true;
-- name: GetAllLeagues :many -- name: GetAllLeagues :many
SELECT id, SELECT id,
name, name,
country_code, country_code,
bet365_id, bet365_id,
is_active is_active,
FROM leagues; sport_id
FROM leagues
WHERE (
country_code = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL
)
AND (
sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL
)
AND (
is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') IS NULL
)
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: CheckLeagueSupport :one -- name: CheckLeagueSupport :one
SELECT EXISTS( SELECT EXISTS(
SELECT 1 SELECT 1
@ -36,11 +44,20 @@ SELECT EXISTS(
); );
-- name: UpdateLeague :exec -- name: UpdateLeague :exec
UPDATE leagues UPDATE leagues
SET name = $1, SET name = COALESCE(sqlc.narg('name'), name),
country_code = $2, country_code = COALESCE(sqlc.narg('country_code'), country_code),
bet365_id = $3, bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id),
is_active = $4 is_active = COALESCE(sqlc.narg('is_active'), is_active),
WHERE id = $5; sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
WHERE id = $1;
-- name: UpdateLeagueByBet365ID :exec
UPDATE leagues
SET name = COALESCE(sqlc.narg('name'), name),
id = COALESCE(sqlc.narg('id'), id),
country_code = COALESCE(sqlc.narg('country_code'), country_code),
is_active = COALESCE(sqlc.narg('is_active'), is_active),
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
WHERE bet365_id = $1;
-- name: SetLeagueActive :exec -- name: SetLeagueActive :exec
UPDATE leagues UPDATE leagues
SET is_active = $2 SET is_active = $2

View File

@ -63,7 +63,7 @@ SELECT event_id,
is_active is_active
FROM odds FROM odds
WHERE is_active = true WHERE is_active = true
AND source = 'b365api'; AND source = 'bet365';
-- name: GetALLPrematchOdds :many -- name: GetALLPrematchOdds :many
SELECT event_id, SELECT event_id,
fi, fi,
@ -82,7 +82,7 @@ SELECT event_id,
is_active is_active
FROM odds FROM odds
WHERE is_active = true WHERE is_active = true
AND source = 'b365api'; AND source = 'bet365';
-- name: GetRawOddsByMarketID :one -- name: GetRawOddsByMarketID :one
SELECT id, SELECT id,
market_name, market_name,
@ -93,7 +93,7 @@ FROM odds
WHERE market_id = $1 WHERE market_id = $1
AND fi = $2 AND fi = $2
AND is_active = true AND is_active = true
AND source = 'b365api'; AND source = 'bet365';
-- name: GetPrematchOddsByUpcomingID :many -- name: GetPrematchOddsByUpcomingID :many
SELECT o.* SELECT o.*
FROM odds o FROM odds o
@ -102,7 +102,7 @@ WHERE e.id = $1
AND e.is_live = false AND e.is_live = false
AND e.status = 'upcoming' AND e.status = 'upcoming'
AND o.is_active = true AND o.is_active = true
AND o.source = 'b365api'; AND o.source = 'bet365';
-- name: GetPaginatedPrematchOddsByUpcomingID :many -- name: GetPaginatedPrematchOddsByUpcomingID :many
SELECT o.* SELECT o.*
FROM odds o FROM odds o
@ -111,5 +111,5 @@ WHERE e.id = $1
AND e.is_live = false AND e.is_live = false
AND e.status = 'upcoming' AND e.status = 'upcoming'
AND o.is_active = true AND o.is_active = true
AND o.source = 'b365api' AND o.source = 'bet365'
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');

View File

@ -105,24 +105,25 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]GetAllUpcomingEve
} }
const GetExpiredUpcomingEvents = `-- name: GetExpiredUpcomingEvents :many const GetExpiredUpcomingEvents = `-- name: GetExpiredUpcomingEvents :many
SELECT id, SELECT events.id,
sport_id, events.sport_id,
match_name, events.match_name,
home_team, events.home_team,
away_team, events.away_team,
home_team_id, events.home_team_id,
away_team_id, events.away_team_id,
home_kit_image, events.home_kit_image,
away_kit_image, events.away_kit_image,
league_id, events.league_id,
league_name, events.league_name,
league_cc, events.start_time,
start_time, events.is_live,
is_live, events.status,
status, events.source,
source, events.fetched_at,
fetched_at leagues.country_code as league_cc
FROM events FROM events
LEFT JOIN leagues ON leagues.id = league_id
WHERE start_time < now() WHERE start_time < now()
and ( and (
status = $1 status = $1
@ -143,12 +144,12 @@ type GetExpiredUpcomingEventsRow struct {
AwayKitImage pgtype.Text `json:"away_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"`
LeagueID pgtype.Int4 `json:"league_id"` LeagueID pgtype.Int4 `json:"league_id"`
LeagueName pgtype.Text `json:"league_name"` LeagueName pgtype.Text `json:"league_name"`
LeagueCc pgtype.Text `json:"league_cc"`
StartTime pgtype.Timestamp `json:"start_time"` StartTime pgtype.Timestamp `json:"start_time"`
IsLive pgtype.Bool `json:"is_live"` IsLive pgtype.Bool `json:"is_live"`
Status pgtype.Text `json:"status"` Status pgtype.Text `json:"status"`
Source pgtype.Text `json:"source"` Source pgtype.Text `json:"source"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
LeagueCc pgtype.Text `json:"league_cc"`
} }
func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Text) ([]GetExpiredUpcomingEventsRow, error) { func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Text) ([]GetExpiredUpcomingEventsRow, error) {
@ -172,12 +173,12 @@ func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Te
&i.AwayKitImage, &i.AwayKitImage,
&i.LeagueID, &i.LeagueID,
&i.LeagueName, &i.LeagueName,
&i.LeagueCc,
&i.StartTime, &i.StartTime,
&i.IsLive, &i.IsLive,
&i.Status, &i.Status,
&i.Source, &i.Source,
&i.FetchedAt, &i.FetchedAt,
&i.LeagueCc,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -190,24 +191,25 @@ func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Te
} }
const GetPaginatedUpcomingEvents = `-- name: GetPaginatedUpcomingEvents :many const GetPaginatedUpcomingEvents = `-- name: GetPaginatedUpcomingEvents :many
SELECT id, SELECT events.id,
sport_id, events.sport_id,
match_name, events.match_name,
home_team, events.home_team,
away_team, events.away_team,
home_team_id, events.home_team_id,
away_team_id, events.away_team_id,
home_kit_image, events.home_kit_image,
away_kit_image, events.away_kit_image,
league_id, events.league_id,
league_name, events.league_name,
league_cc, events.start_time,
start_time, events.is_live,
is_live, events.status,
status, events.source,
source, events.fetched_at,
fetched_at leagues.country_code as league_cc
FROM events FROM events
LEFT JOIN leagues ON leagues.id = league_id
WHERE start_time > now() WHERE start_time > now()
AND is_live = false AND is_live = false
AND status = 'upcoming' AND status = 'upcoming'
@ -216,7 +218,7 @@ WHERE start_time > now()
OR $1 IS NULL OR $1 IS NULL
) )
AND ( AND (
sport_id = $2 events.sport_id = $2
OR $2 IS NULL OR $2 IS NULL
) )
AND ( AND (
@ -227,8 +229,12 @@ WHERE start_time > now()
start_time > $4 start_time > $4
OR $4 IS NULL OR $4 IS NULL
) )
AND (
leagues.country_code = $5
OR $5 IS NULL
)
ORDER BY start_time ASC ORDER BY start_time ASC
LIMIT $6 OFFSET $5 LIMIT $7 OFFSET $6
` `
type GetPaginatedUpcomingEventsParams struct { type GetPaginatedUpcomingEventsParams struct {
@ -236,6 +242,7 @@ type GetPaginatedUpcomingEventsParams struct {
SportID pgtype.Int4 `json:"sport_id"` SportID pgtype.Int4 `json:"sport_id"`
LastStartTime pgtype.Timestamp `json:"last_start_time"` LastStartTime pgtype.Timestamp `json:"last_start_time"`
FirstStartTime pgtype.Timestamp `json:"first_start_time"` FirstStartTime pgtype.Timestamp `json:"first_start_time"`
CountryCode pgtype.Text `json:"country_code"`
Offset pgtype.Int4 `json:"offset"` Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"` Limit pgtype.Int4 `json:"limit"`
} }
@ -252,12 +259,12 @@ type GetPaginatedUpcomingEventsRow struct {
AwayKitImage pgtype.Text `json:"away_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"`
LeagueID pgtype.Int4 `json:"league_id"` LeagueID pgtype.Int4 `json:"league_id"`
LeagueName pgtype.Text `json:"league_name"` LeagueName pgtype.Text `json:"league_name"`
LeagueCc pgtype.Text `json:"league_cc"`
StartTime pgtype.Timestamp `json:"start_time"` StartTime pgtype.Timestamp `json:"start_time"`
IsLive pgtype.Bool `json:"is_live"` IsLive pgtype.Bool `json:"is_live"`
Status pgtype.Text `json:"status"` Status pgtype.Text `json:"status"`
Source pgtype.Text `json:"source"` Source pgtype.Text `json:"source"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
LeagueCc pgtype.Text `json:"league_cc"`
} }
func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginatedUpcomingEventsParams) ([]GetPaginatedUpcomingEventsRow, error) { func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginatedUpcomingEventsParams) ([]GetPaginatedUpcomingEventsRow, error) {
@ -266,6 +273,7 @@ func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginat
arg.SportID, arg.SportID,
arg.LastStartTime, arg.LastStartTime,
arg.FirstStartTime, arg.FirstStartTime,
arg.CountryCode,
arg.Offset, arg.Offset,
arg.Limit, arg.Limit,
) )
@ -288,12 +296,12 @@ func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginat
&i.AwayKitImage, &i.AwayKitImage,
&i.LeagueID, &i.LeagueID,
&i.LeagueName, &i.LeagueName,
&i.LeagueCc,
&i.StartTime, &i.StartTime,
&i.IsLive, &i.IsLive,
&i.Status, &i.Status,
&i.Source, &i.Source,
&i.FetchedAt, &i.FetchedAt,
&i.LeagueCc,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -308,6 +316,7 @@ func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginat
const GetTotalEvents = `-- name: GetTotalEvents :one const GetTotalEvents = `-- name: GetTotalEvents :one
SELECT COUNT(*) SELECT COUNT(*)
FROM events FROM events
LEFT JOIN leagues ON leagues.id = league_id
WHERE is_live = false WHERE is_live = false
AND status = 'upcoming' AND status = 'upcoming'
AND ( AND (
@ -315,7 +324,7 @@ WHERE is_live = false
OR $1 IS NULL OR $1 IS NULL
) )
AND ( AND (
sport_id = $2 events.sport_id = $2
OR $2 IS NULL OR $2 IS NULL
) )
AND ( AND (
@ -326,6 +335,10 @@ WHERE is_live = false
start_time > $4 start_time > $4
OR $4 IS NULL OR $4 IS NULL
) )
AND (
leagues.country_code = $5
OR $5 IS NULL
)
` `
type GetTotalEventsParams struct { type GetTotalEventsParams struct {
@ -333,6 +346,7 @@ type GetTotalEventsParams struct {
SportID pgtype.Int4 `json:"sport_id"` SportID pgtype.Int4 `json:"sport_id"`
LastStartTime pgtype.Timestamp `json:"last_start_time"` LastStartTime pgtype.Timestamp `json:"last_start_time"`
FirstStartTime pgtype.Timestamp `json:"first_start_time"` FirstStartTime pgtype.Timestamp `json:"first_start_time"`
CountryCode pgtype.Text `json:"country_code"`
} }
func (q *Queries) GetTotalEvents(ctx context.Context, arg GetTotalEventsParams) (int64, error) { func (q *Queries) GetTotalEvents(ctx context.Context, arg GetTotalEventsParams) (int64, error) {
@ -341,6 +355,7 @@ func (q *Queries) GetTotalEvents(ctx context.Context, arg GetTotalEventsParams)
arg.SportID, arg.SportID,
arg.LastStartTime, arg.LastStartTime,
arg.FirstStartTime, arg.FirstStartTime,
arg.CountryCode,
) )
var count int64 var count int64
err := row.Scan(&count) err := row.Scan(&count)

View File

@ -32,61 +32,63 @@ SELECT id,
name, name,
country_code, country_code,
bet365_id, bet365_id,
is_active is_active,
sport_id
FROM leagues FROM leagues
WHERE (
country_code = $1
OR $1 IS NULL
)
AND (
sport_id = $2
OR $2 IS NULL
)
AND (
is_active = $3
OR $3 IS NULL
)
LIMIT $5 OFFSET $4
` `
func (q *Queries) GetAllLeagues(ctx context.Context) ([]League, error) { type GetAllLeaguesParams struct {
rows, err := q.db.Query(ctx, GetAllLeagues) CountryCode pgtype.Text `json:"country_code"`
SportID pgtype.Int4 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
type GetAllLeaguesRow struct {
ID int64 `json:"id"`
Name string `json:"name"`
CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"`
IsActive pgtype.Bool `json:"is_active"`
SportID int32 `json:"sport_id"`
}
func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([]GetAllLeaguesRow, error) {
rows, err := q.db.Query(ctx, GetAllLeagues,
arg.CountryCode,
arg.SportID,
arg.IsActive,
arg.Offset,
arg.Limit,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []League var items []GetAllLeaguesRow
for rows.Next() { for rows.Next() {
var i League var i GetAllLeaguesRow
if err := rows.Scan(
&i.ID,
&i.Name,
&i.CountryCode,
&i.Bet365ID,
&i.IsActive,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetSupportedLeagues = `-- name: GetSupportedLeagues :many
SELECT id,
name,
country_code,
bet365_id,
is_active
FROM leagues
WHERE is_active = true
`
func (q *Queries) GetSupportedLeagues(ctx context.Context) ([]League, error) {
rows, err := q.db.Query(ctx, GetSupportedLeagues)
if err != nil {
return nil, err
}
defer rows.Close()
var items []League
for rows.Next() {
var i League
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.CountryCode, &i.CountryCode,
&i.Bet365ID, &i.Bet365ID,
&i.IsActive, &i.IsActive,
&i.SportID,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -104,14 +106,16 @@ INSERT INTO leagues (
name, name,
country_code, country_code,
bet365_id, bet365_id,
sport_id,
is_active is_active
) )
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (id) DO VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO
UPDATE UPDATE
SET name = EXCLUDED.name, SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code, country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id, bet365_id = EXCLUDED.bet365_id,
is_active = EXCLUDED.is_active is_active = EXCLUDED.is_active,
sport_id = EXCLUDED.sport_id
` `
type InsertLeagueParams struct { type InsertLeagueParams struct {
@ -119,6 +123,7 @@ type InsertLeagueParams struct {
Name string `json:"name"` Name string `json:"name"`
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"` Bet365ID pgtype.Int4 `json:"bet365_id"`
SportID int32 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
} }
@ -128,6 +133,7 @@ func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) erro
arg.Name, arg.Name,
arg.CountryCode, arg.CountryCode,
arg.Bet365ID, arg.Bet365ID,
arg.SportID,
arg.IsActive, arg.IsActive,
) )
return err return err
@ -151,28 +157,62 @@ func (q *Queries) SetLeagueActive(ctx context.Context, arg SetLeagueActiveParams
const UpdateLeague = `-- name: UpdateLeague :exec const UpdateLeague = `-- name: UpdateLeague :exec
UPDATE leagues UPDATE leagues
SET name = $1, SET name = COALESCE($2, name),
country_code = $2, country_code = COALESCE($3, country_code),
bet365_id = $3, bet365_id = COALESCE($4, bet365_id),
is_active = $4 is_active = COALESCE($5, is_active),
WHERE id = $5 sport_id = COALESCE($6, sport_id)
WHERE id = $1
` `
type UpdateLeagueParams struct { type UpdateLeagueParams struct {
Name string `json:"name"` ID int64 `json:"id"`
Name pgtype.Text `json:"name"`
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"` Bet365ID pgtype.Int4 `json:"bet365_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
ID int64 `json:"id"` SportID pgtype.Int4 `json:"sport_id"`
} }
func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) error { func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) error {
_, err := q.db.Exec(ctx, UpdateLeague, _, err := q.db.Exec(ctx, UpdateLeague,
arg.ID,
arg.Name, arg.Name,
arg.CountryCode, arg.CountryCode,
arg.Bet365ID, arg.Bet365ID,
arg.IsActive, arg.IsActive,
arg.ID, arg.SportID,
)
return err
}
const UpdateLeagueByBet365ID = `-- name: UpdateLeagueByBet365ID :exec
UPDATE leagues
SET name = COALESCE($2, name),
id = COALESCE($3, id),
country_code = COALESCE($4, country_code),
is_active = COALESCE($5, is_active),
sport_id = COALESCE($6, sport_id)
WHERE bet365_id = $1
`
type UpdateLeagueByBet365IDParams struct {
Bet365ID pgtype.Int4 `json:"bet365_id"`
Name pgtype.Text `json:"name"`
ID pgtype.Int8 `json:"id"`
CountryCode pgtype.Text `json:"country_code"`
IsActive pgtype.Bool `json:"is_active"`
SportID pgtype.Int4 `json:"sport_id"`
}
func (q *Queries) UpdateLeagueByBet365ID(ctx context.Context, arg UpdateLeagueByBet365IDParams) error {
_, err := q.db.Exec(ctx, UpdateLeagueByBet365ID,
arg.Bet365ID,
arg.Name,
arg.ID,
arg.CountryCode,
arg.IsActive,
arg.SportID,
) )
return err return err
} }

View File

@ -207,6 +207,7 @@ type League struct {
Name string `json:"name"` Name string `json:"name"`
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"` Bet365ID pgtype.Int4 `json:"bet365_id"`
SportID int32 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
} }

View File

@ -29,7 +29,7 @@ SELECT event_id,
is_active is_active
FROM odds FROM odds
WHERE is_active = true WHERE is_active = true
AND source = 'b365api' AND source = 'bet365'
` `
type GetALLPrematchOddsRow struct { type GetALLPrematchOddsRow struct {
@ -94,7 +94,7 @@ WHERE e.id = $1
AND e.is_live = false AND e.is_live = false
AND e.status = 'upcoming' AND e.status = 'upcoming'
AND o.is_active = true AND o.is_active = true
AND o.source = 'b365api' AND o.source = 'bet365'
LIMIT $3 OFFSET $2 LIMIT $3 OFFSET $2
` `
@ -159,7 +159,7 @@ SELECT event_id,
is_active is_active
FROM odds FROM odds
WHERE is_active = true WHERE is_active = true
AND source = 'b365api' AND source = 'bet365'
` `
type GetPrematchOddsRow struct { type GetPrematchOddsRow struct {
@ -224,7 +224,7 @@ WHERE e.id = $1
AND e.is_live = false AND e.is_live = false
AND e.status = 'upcoming' AND e.status = 'upcoming'
AND o.is_active = true AND o.is_active = true
AND o.source = 'b365api' AND o.source = 'bet365'
` `
func (q *Queries) GetPrematchOddsByUpcomingID(ctx context.Context, id string) ([]Odd, error) { func (q *Queries) GetPrematchOddsByUpcomingID(ctx context.Context, id string) ([]Odd, error) {
@ -274,7 +274,7 @@ FROM odds
WHERE market_id = $1 WHERE market_id = $1
AND fi = $2 AND fi = $2
AND is_active = true AND is_active = true
AND source = 'b365api' AND source = 'bet365'
` `
type GetRawOddsByMarketIDParams struct { type GetRawOddsByMarketIDParams struct {

View File

@ -121,6 +121,7 @@ type Odds struct {
type EventFilter struct { type EventFilter struct {
SportID ValidInt32 SportID ValidInt32
LeagueID ValidInt32 LeagueID ValidInt32
CountryCode ValidString
FirstStartTime ValidTime FirstStartTime ValidTime
LastStartTime ValidTime LastStartTime ValidTime
Limit ValidInt64 Limit ValidInt64

View File

@ -6,4 +6,22 @@ type League struct {
CountryCode string `json:"cc" example:"uk"` CountryCode string `json:"cc" example:"uk"`
Bet365ID int32 `json:"bet365_id" example:"1121"` Bet365ID int32 `json:"bet365_id" example:"1121"`
IsActive bool `json:"is_active" example:"false"` IsActive bool `json:"is_active" example:"false"`
SportID int32 `json:"sport_id" example:"1"`
}
type UpdateLeague struct {
ID int64 `json:"id" example:"1"`
Name ValidString `json:"name" example:"BPL"`
CountryCode ValidString `json:"cc" example:"uk"`
Bet365ID ValidInt32 `json:"bet365_id" example:"1121"`
IsActive ValidBool `json:"is_active" example:"false"`
SportID ValidInt32 `json:"sport_id" example:"1"`
}
type LeagueFilter struct {
CountryCode ValidString
SportID ValidInt32
IsActive ValidBool
Limit ValidInt64
Offset ValidInt64
} }

View File

@ -32,7 +32,7 @@ type CommonResultResponse struct {
SportID string `json:"sport_id"` SportID string `json:"sport_id"`
Time string `json:"time"` Time string `json:"time"`
TimeStatus string `json:"time_status"` TimeStatus string `json:"time_status"`
League League `json:"league"` League LeagueRes `json:"league"`
Home Team `json:"home"` Home Team `json:"home"`
Away Team `json:"away"` Away Team `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`

View File

@ -153,6 +153,10 @@ func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.Ev
Time: filter.LastStartTime.Value.UTC(), Time: filter.LastStartTime.Value.UTC(),
Valid: filter.LastStartTime.Valid, Valid: filter.LastStartTime.Valid,
}, },
CountryCode: pgtype.Text{
String: filter.CountryCode.Value,
Valid: filter.CountryCode.Valid,
},
}) })
if err != nil { if err != nil {
@ -195,6 +199,10 @@ func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.Ev
Time: filter.LastStartTime.Value.UTC(), Time: filter.LastStartTime.Value.UTC(),
Valid: filter.LastStartTime.Valid, Valid: filter.LastStartTime.Valid,
}, },
CountryCode: pgtype.Text{
String: filter.CountryCode.Value,
Valid: filter.CountryCode.Valid,
},
}) })
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err

View File

@ -15,30 +15,33 @@ func (s *Store) SaveLeague(ctx context.Context, l domain.League) error {
CountryCode: pgtype.Text{String: l.CountryCode, Valid: true}, CountryCode: pgtype.Text{String: l.CountryCode, Valid: true},
Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true}, Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true},
IsActive: pgtype.Bool{Bool: l.IsActive, Valid: true}, IsActive: pgtype.Bool{Bool: l.IsActive, Valid: true},
SportID: l.SportID,
}) })
} }
func (s *Store) GetSupportedLeagues(ctx context.Context) ([]domain.League, error) { func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error) {
leagues, err := s.queries.GetSupportedLeagues(ctx) l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{
if err != nil { CountryCode: pgtype.Text{
return nil, err String: filter.CountryCode.Value,
} Valid: filter.CountryCode.Valid,
},
supportedLeagues := make([]domain.League, len(leagues)) SportID: pgtype.Int4{
for i, league := range leagues { Int32: filter.SportID.Value,
supportedLeagues[i] = domain.League{ Valid: filter.SportID.Valid,
ID: league.ID, },
Name: league.Name, IsActive: pgtype.Bool{
CountryCode: league.CountryCode.String, Bool: filter.IsActive.Value,
Bet365ID: league.Bet365ID.Int32, Valid: filter.IsActive.Valid,
IsActive: league.IsActive.Bool, },
} Limit: pgtype.Int4{
} Int32: int32(filter.Limit.Value),
return supportedLeagues, nil Valid: filter.Limit.Valid,
} },
Offset: pgtype.Int4{
func (s *Store) GetAllLeagues(ctx context.Context) ([]domain.League, error) { Int32: int32(filter.Offset.Value * filter.Limit.Value),
l, err := s.queries.GetAllLeagues(ctx) Valid: filter.Offset.Valid,
},
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -51,6 +54,7 @@ func (s *Store) GetAllLeagues(ctx context.Context) ([]domain.League, error) {
CountryCode: league.CountryCode.String, CountryCode: league.CountryCode.String,
Bet365ID: league.Bet365ID.Int32, Bet365ID: league.Bet365ID.Int32,
IsActive: league.IsActive.Bool, IsActive: league.IsActive.Bool,
SportID: league.SportID,
} }
} }
return leagues, nil return leagues, nil
@ -70,12 +74,30 @@ func (s *Store) SetLeagueActive(ctx context.Context, leagueId int64, isActive bo
}) })
} }
// TODO: update based on id, no need for the entire league (same as the set active one) func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {
func (s *Store) SetLeagueInActive(ctx context.Context, l domain.League) error { err := s.queries.UpdateLeague(ctx, dbgen.UpdateLeagueParams{
return s.queries.UpdateLeague(ctx, dbgen.UpdateLeagueParams{ ID: league.ID,
Name: l.Name, Name: pgtype.Text{
CountryCode: pgtype.Text{String: l.CountryCode, Valid: true}, String: league.Name.Value,
Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true}, Valid: league.Name.Valid,
IsActive: pgtype.Bool{Bool: false, Valid: true}, },
CountryCode: pgtype.Text{
String: league.CountryCode.Value,
Valid: league.CountryCode.Valid,
},
Bet365ID: pgtype.Int4{
Int32: league.Bet365ID.Value,
Valid: league.Bet365ID.Valid,
},
IsActive: pgtype.Bool{
Bool: league.IsActive.Value,
Valid: league.IsActive.Valid,
},
SportID: pgtype.Int4{
Int32: league.SportID.Value,
Valid: league.SportID.Valid,
},
}) })
return err
} }

View File

@ -3,6 +3,7 @@ package repository
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"os" "os"
"strconv" "strconv"
"time" "time"

View File

@ -209,16 +209,17 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, sour
// log.Printf("❌ Failed to open leagues file %v", err) // log.Printf("❌ Failed to open leagues file %v", err)
// return // return
// } // }
for _, sportID := range sportIDs { for sportIndex, sportID := range sportIDs {
var totalPages int = 1 var totalPages int = 1
var page int = 0 var page int = 0
var limit int = 100 var limit int = 200
var count int = 0 var count int = 0
log.Printf("Sport ID %d", sportID) log.Printf("Sport ID %d", sportID)
for page <= totalPages { for page <= totalPages {
page = page + 1 page = page + 1
url := fmt.Sprintf(url, sportID, s.token, page) url := fmt.Sprintf(url, sportID, s.token, page)
log.Printf("📡 Fetching data from %s - sport %d, for event data page %d", source, sportID, page) log.Printf("📡 Fetching data from %s - sport %d (%d/%d), for event data page (%d/%d)",
source, sportID, sportIndex+1, len(sportIDs), page, totalPages)
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
log.Printf("❌ Failed to fetch event data for page %d: %v", page, err) log.Printf("❌ Failed to fetch event data for page %d: %v", page, err)
@ -249,13 +250,23 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, sour
} }
// doesn't make sense to save and check back to back, but for now it can be here // doesn't make sense to save and check back to back, but for now it can be here
s.store.SaveLeague(ctx, domain.League{ // no this its fine to keep it here
// but change the league id to bet365 id later
err = s.store.SaveLeague(ctx, domain.League{
ID: leagueID, ID: leagueID,
Name: ev.League.Name, Name: ev.League.Name,
IsActive: true, IsActive: true,
SportID: convertInt32(ev.SportID),
}) })
if err != nil {
log.Printf("❌ Error Saving League on %v", ev.League.Name)
log.Printf("err:%v", err)
continue
}
if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil { if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil {
log.Printf("Skipping league %v", ev.League.Name)
skippedLeague = append(skippedLeague, ev.League.Name) skippedLeague = append(skippedLeague, ev.League.Name)
continue continue
} }

View File

@ -7,6 +7,8 @@ import (
) )
type Service interface { type Service interface {
GetAllLeagues(ctx context.Context) ([]domain.League, error) SaveLeague(ctx context.Context, l domain.League) error
GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error)
SetLeagueActive(ctx context.Context, leagueId int64, isActive bool) error SetLeagueActive(ctx context.Context, leagueId int64, isActive bool) error
UpdateLeague(ctx context.Context, league domain.UpdateLeague) error
} }

View File

@ -17,10 +17,18 @@ func New(store *repository.Store) Service {
} }
} }
func (s *service) GetAllLeagues(ctx context.Context) ([]domain.League, error) { func (s *service) SaveLeague(ctx context.Context, l domain.League) error {
return s.store.GetAllLeagues(ctx) return s.store.SaveLeague(ctx, l)
}
func (s *service) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error) {
return s.store.GetAllLeagues(ctx, filter)
} }
func (s *service) SetLeagueActive(ctx context.Context, leagueId int64, isActive bool) error { func (s *service) SetLeagueActive(ctx context.Context, leagueId int64, isActive bool) error {
return s.store.SetLeagueActive(ctx, leagueId, isActive) return s.store.SetLeagueActive(ctx, leagueId, isActive)
} }
func (s *service) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {
return s.store.UpdateLeague(ctx, league)
}

View File

@ -41,18 +41,23 @@ func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error {
// wg.Add(2) // wg.Add(2)
wg.Add(1) wg.Add(1)
go func() {
defer wg.Done()
if err := s.fetchBet365Odds(ctx); err != nil {
errChan <- fmt.Errorf("bet365 odds fetching error: %w", err)
}
}()
// go func() { // go func() {
// defer wg.Done() // defer wg.Done()
// if err := s.fetchBet365Odds(ctx); err != nil { // if err := s.fetchBwinOdds(ctx); err != nil {
// errChan <- fmt.Errorf("bet365 odds fetching error: %w", err) // errChan <- fmt.Errorf("bwin odds fetching error: %w", err)
// } // }
// }() // }()
go func() { go func() {
defer wg.Done() wg.Wait()
if err := s.fetchBwinOdds(ctx); err != nil { close(errChan)
errChan <- fmt.Errorf("bwin odds fetching error: %w", err)
}
}() }()
var errs []error var errs []error
@ -118,7 +123,11 @@ func (s *ServiceImpl) fetchBet365Odds(ctx context.Context) error {
} }
return errors.Join(errs...) for err := range errs {
log.Printf("❌ Error: %v", err)
}
return nil
} }
func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error { func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error {
@ -208,7 +217,6 @@ func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error {
return nil return nil
} }
func (s *ServiceImpl) FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error) { func (s *ServiceImpl) FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error) {
eventID, err := strconv.ParseInt(eventIDStr, 10, 64) eventID, err := strconv.ParseInt(eventIDStr, 10, 64)

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"log/slog" "log/slog"
"net/http" "net/http"
"strconv" "strconv"
@ -15,6 +16,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository" "github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
) )
@ -26,9 +28,10 @@ type Service struct {
betSvc bet.Service betSvc bet.Service
oddSvc odds.ServiceImpl oddSvc odds.ServiceImpl
eventSvc event.Service eventSvc event.Service
leagueSvc league.Service
} }
func NewService(repo *repository.Store, cfg *config.Config, logger *slog.Logger, betSvc bet.Service, oddSvc odds.ServiceImpl, eventSvc event.Service) *Service { func NewService(repo *repository.Store, cfg *config.Config, logger *slog.Logger, betSvc bet.Service, oddSvc odds.ServiceImpl, eventSvc event.Service, leagueSvc league.Service) *Service {
return &Service{ return &Service{
repo: repo, repo: repo,
config: cfg, config: cfg,
@ -37,6 +40,7 @@ func NewService(repo *repository.Store, cfg *config.Config, logger *slog.Logger,
betSvc: betSvc, betSvc: betSvc,
oddSvc: oddSvc, oddSvc: oddSvc,
eventSvc: eventSvc, eventSvc: eventSvc,
leagueSvc: leagueSvc,
} }
} }
@ -274,6 +278,33 @@ func (s *Service) CheckAndUpdateExpiredEvents(ctx context.Context) (int64, error
} }
updated++ updated++
fmt.Printf("✅ Successfully updated event %v to %v (%d/%d) \n", event.ID, eventStatus, i+1, len(events)) fmt.Printf("✅ Successfully updated event %v to %v (%d/%d) \n", event.ID, eventStatus, i+1, len(events))
// Update the league because the league country code is only found on the result response
leagueID, err := strconv.ParseInt(commonResp.League.ID, 10, 64)
if err != nil {
log.Printf("❌ Invalid league id, leagueID %v", commonResp.League.ID)
continue
}
err = s.leagueSvc.UpdateLeague(ctx, domain.UpdateLeague{
ID: int64(event.LeagueID),
CountryCode: domain.ValidString{
Value: commonResp.League.CC,
Valid: true,
},
Bet365ID: domain.ValidInt32{
Value: int32(leagueID),
Valid: true,
},
})
if err != nil {
log.Printf("❌ Error Updating League %v", commonResp.League.Name)
log.Printf("err:%v", err)
continue
}
fmt.Printf("✅ Updated League %v with country code %v \n", leagueID, commonResp.League.CC)
} }
if updated == 0 { if updated == 0 {

View File

@ -30,7 +30,7 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
// }, // },
// }, // },
// { // {
// spec: "0 */15 * * * *", // Every 15 minutes // spec: "0 0 * * * *", // Every 15 minutes
// task: func() { // task: func() {
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil { // if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
// log.Printf("FetchNonLiveOdds error: %v", err) // log.Printf("FetchNonLiveOdds error: %v", err)
@ -40,7 +40,7 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
// { // {
// spec: "0 */5 * * * *", // Every 5 Minutes // spec: "0 */5 * * * *", // Every 5 Minutes
// task: func() { // task: func() {
// log.Println("Updating expired events...") // log.Println("Updating expired events status...")
// if _, err := resultService.CheckAndUpdateExpiredEvents(context.Background()); err != nil { // if _, err := resultService.CheckAndUpdateExpiredEvents(context.Background()); err != nil {
// log.Printf("Failed to update events: %v", err) // log.Printf("Failed to update events: %v", err)

View File

@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"fmt"
"strconv" "strconv"
"time" "time"
@ -23,7 +24,7 @@ import (
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /bet [post] // @Router /bet [post]
func (h *Handler) CreateBet(c *fiber.Ctx) error { func (h *Handler) CreateBet(c *fiber.Ctx) error {
fmt.Printf("Calling leagues")
// Get user_id from middleware // Get user_id from middleware
userID := c.Locals("user_id").(int64) userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role) role := c.Locals("role").(domain.Role)

View File

@ -1,19 +1,77 @@
package handlers package handlers
import ( import (
"fmt"
"strconv" "strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
// GetAllLeagues godoc
// @Summary Gets all leagues
// @Description Gets all leagues
// @Tags leagues
// @Accept json
// @Produce json
// @Success 200 {array} domain.League
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /leagues [get]
func (h *Handler) GetAllLeagues(c *fiber.Ctx) error { func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
leagues, err := h.leagueSvc.GetAllLeagues(c.Context()) page := c.QueryInt("page", 1)
if err != nil { pageSize := c.QueryInt("page_size", 10)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get leagues", err, nil)
limit := domain.ValidInt64{
Value: int64(pageSize),
Valid: pageSize == 0,
}
offset := domain.ValidInt64{
Value: int64(page - 1),
Valid: true,
} }
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrived", leagues, nil) countryCodeQuery := c.Query("cc")
countryCode := domain.ValidString{
Value: countryCodeQuery,
Valid: countryCodeQuery != "",
}
isActiveQuery := c.QueryBool("is_active", false)
isActiveFilter := c.QueryBool("is_active_filter", false)
isActive := domain.ValidBool{
Value: isActiveQuery,
Valid: isActiveFilter,
}
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil {
h.logger.Error("invalid sport id", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "invalid sport id", nil, nil)
}
sportID = domain.ValidInt32{
Value: int32(sportIDint),
Valid: true,
}
}
leagues, err := h.leagueSvc.GetAllLeagues(c.Context(), domain.LeagueFilter{
CountryCode: countryCode,
IsActive: isActive,
SportID: sportID,
Limit: limit,
Offset: offset,
})
if err != nil {
fmt.Printf("Error fetching league %v \n", err)
h.logger.Error("Failed to get leagues", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get leagues", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", leagues, nil)
} }
type SetLeagueActiveReq struct { type SetLeagueActiveReq struct {
@ -21,6 +79,7 @@ type SetLeagueActiveReq struct {
} }
func (h *Handler) SetLeagueActive(c *fiber.Ctx) error { func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
fmt.Printf("Set Active Leagues")
leagueIdStr := c.Params("id") leagueIdStr := c.Params("id")
if leagueIdStr == "" { if leagueIdStr == "" {
response.WriteJSON(c, fiber.StatusBadRequest, "Missing league id", nil, nil) response.WriteJSON(c, fiber.StatusBadRequest, "Missing league id", nil, nil)

View File

@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"fmt"
"strconv" "strconv"
"time" "time"
@ -9,33 +10,6 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
// GetPrematchOdds godoc
// @Summary Retrieve prematch odds for an event
// @Description Retrieve prematch odds for a specific event by event ID
// @Tags prematch
// @Accept json
// @Produce json
// @Param event_id path string true "Event ID"
// @Success 200 {array} domain.Odd
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /prematch/odds/{event_id} [get]
func (h *Handler) GetPrematchOdds(c *fiber.Ctx) error {
eventID := c.Params("event_id")
if eventID == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing event_id", nil, nil)
}
odds, err := h.prematchSvc.GetPrematchOdds(c.Context(), eventID)
if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve odds", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil)
}
// GetALLPrematchOdds // GetALLPrematchOdds
// @Summary Retrieve all prematch odds // @Summary Retrieve all prematch odds
// @Description Retrieve all prematch odds from the database // @Description Retrieve all prematch odds from the database
@ -44,7 +18,7 @@ func (h *Handler) GetPrematchOdds(c *fiber.Ctx) error {
// @Produce json // @Produce json
// @Success 200 {array} domain.Odd // @Success 200 {array} domain.Odd
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/odds [get] // @Router /odds [get]
func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error { func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error {
odds, err := h.prematchSvc.GetALLPrematchOdds(c.Context()) odds, err := h.prematchSvc.GetALLPrematchOdds(c.Context())
@ -67,7 +41,7 @@ func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error {
// @Success 200 {array} domain.RawOddsByMarketID // @Success 200 {array} domain.RawOddsByMarketID
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/odds/upcoming/{upcoming_id}/market/{market_id} [get] // @Router /odds/upcoming/{upcoming_id}/market/{market_id} [get]
func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error { func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error {
marketID := c.Params("market_id") marketID := c.Params("market_id")
@ -82,7 +56,8 @@ func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error {
rawOdds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketID, upcomingID) rawOdds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketID, upcomingID)
if err != nil { if err != nil {
// h.logger.Error("failed to fetch raw odds", "error", err) fmt.Printf("Failed to fetch raw odds: %v market_id:%v upcomingID:%v\n", err, marketID, upcomingID)
h.logger.Error("failed to fetch raw odds", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve raw odds", err, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve raw odds", err, nil)
} }
@ -99,20 +74,25 @@ func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error {
// @Param page_size query int false "Page size" // @Param page_size query int false "Page size"
// @Param league_id query string false "League ID Filter" // @Param league_id query string false "League ID Filter"
// @Param sport_id query string false "Sport 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 first_start_time query string false "Start Time"
// @Param last_start_time query string false "End Time" // @Param last_start_time query string false "End Time"
// @Success 200 {array} domain.UpcomingEvent // @Success 200 {array} domain.UpcomingEvent
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/events [get] // @Router /events [get]
func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error { func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
page := c.QueryInt("page", 1) page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10) pageSize := c.QueryInt("page_size", 10)
limit := domain.ValidInt64{
Value: int64(pageSize),
Valid: true,
}
offset := domain.ValidInt64{
Value: int64(page - 1),
Valid: true,
}
leagueIDQuery := c.Query("league_id") leagueIDQuery := c.Query("league_id")
sportIDQuery := c.Query("sport_id")
firstStartTimeQuery := c.Query("first_start_time")
lastStartTimeQuery := c.Query("last_start_time")
var leagueID domain.ValidInt32 var leagueID domain.ValidInt32
if leagueIDQuery != "" { if leagueIDQuery != "" {
leagueIDInt, err := strconv.Atoi(leagueIDQuery) leagueIDInt, err := strconv.Atoi(leagueIDQuery)
@ -125,6 +105,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
Valid: true, Valid: true,
} }
} }
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32 var sportID domain.ValidInt32
if sportIDQuery != "" { if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery) sportIDint, err := strconv.Atoi(sportIDQuery)
@ -137,7 +118,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
Valid: true, Valid: true,
} }
} }
firstStartTimeQuery := c.Query("first_start_time")
var firstStartTime domain.ValidTime var firstStartTime domain.ValidTime
if firstStartTimeQuery != "" { if firstStartTimeQuery != "" {
firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery) firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
@ -150,6 +131,8 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
Valid: true, Valid: true,
} }
} }
lastStartTimeQuery := c.Query("last_start_time")
var lastStartTime domain.ValidTime var lastStartTime domain.ValidTime
if lastStartTimeQuery != "" { if lastStartTimeQuery != "" {
lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery) lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery)
@ -163,15 +146,11 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
} }
} }
limit := domain.ValidInt64{ countryCodeQuery := c.Query("cc")
Value: int64(pageSize), countryCode := domain.ValidString{
Valid: true, Value: countryCodeQuery,
Valid: countryCodeQuery != "",
} }
offset := domain.ValidInt64{
Value: int64(page - 1),
Valid: true,
}
events, total, err := h.eventSvc.GetPaginatedUpcomingEvents( events, total, err := h.eventSvc.GetPaginatedUpcomingEvents(
c.Context(), domain.EventFilter{ c.Context(), domain.EventFilter{
SportID: sportID, SportID: sportID,
@ -180,6 +159,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
LastStartTime: lastStartTime, LastStartTime: lastStartTime,
Limit: limit, Limit: limit,
Offset: offset, Offset: offset,
CountryCode: countryCode,
}) })
// fmt.Printf("League ID: %v", leagueID) // fmt.Printf("League ID: %v", leagueID)
@ -201,7 +181,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
// @Success 200 {object} domain.UpcomingEvent // @Success 200 {object} domain.UpcomingEvent
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/events/{id} [get] // @Router /events/{id} [get]
func (h *Handler) GetUpcomingEventByID(c *fiber.Ctx) error { func (h *Handler) GetUpcomingEventByID(c *fiber.Ctx) error {
id := c.Params("id") id := c.Params("id")
@ -229,8 +209,8 @@ func (h *Handler) GetUpcomingEventByID(c *fiber.Ctx) error {
// @Success 200 {array} domain.Odd // @Success 200 {array} domain.Odd
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /prematch/odds/upcoming/{upcoming_id} [get] // @Router /odds/upcoming/{upcoming_id} [get]
func (h *Handler) GetPrematchOddsByUpcomingID(c *fiber.Ctx) error { func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error {
upcomingID := c.Params("upcoming_id") upcomingID := c.Params("upcoming_id")
if upcomingID == "" { if upcomingID == "" {
@ -269,7 +249,7 @@ type UpdateEventStatusReq struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /event/{id}/remove [patch] // @Router /events/{id} [delete]
func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error { func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error {
eventID := c.Params("id") eventID := c.Params("id")
err := h.eventSvc.UpdateEventStatus(c.Context(), eventID, domain.STATUS_REMOVED) err := h.eventSvc.UpdateEventStatus(c.Context(), eventID, domain.STATUS_REMOVED)

View File

@ -116,10 +116,9 @@ func (a *App) initAppRoutes() {
a.fiber.Put("/managers/:id", a.authMiddleware, h.UpdateManagers) a.fiber.Put("/managers/:id", a.authMiddleware, h.UpdateManagers)
a.fiber.Get("/manager/:id/branch", a.authMiddleware, h.GetBranchByManagerID) a.fiber.Get("/manager/:id/branch", a.authMiddleware, h.GetBranchByManagerID)
a.fiber.Get("/odds/upcoming/:event_id", h.GetPrematchOdds)
a.fiber.Get("/odds", h.GetALLPrematchOdds) a.fiber.Get("/odds", h.GetALLPrematchOdds)
a.fiber.Get("/odds/upcoming/:upcoming_id", h.GetOddsByUpcomingID)
a.fiber.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetRawOddsByMarketID) a.fiber.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetRawOddsByMarketID)
a.fiber.Get("/odds/upcoming/:upcoming_id", h.GetPrematchOddsByUpcomingID)
a.fiber.Get("/events", h.GetAllUpcomingEvents) a.fiber.Get("/events", h.GetAllUpcomingEvents)
a.fiber.Get("/events/:id", h.GetUpcomingEventByID) a.fiber.Get("/events/:id", h.GetUpcomingEventByID)