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)
leagueSvc := league.New(store)
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)
referalRepo := repository.NewReferralRepository(store)
vitualGameRepo := repository.NewVirtualGameRepository(store)

View File

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

View File

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

View File

@ -4,29 +4,37 @@ INSERT INTO leagues (
name,
country_code,
bet365_id,
sport_id,
is_active
)
VALUES ($1, $2, $3, $4, $5) ON CONFLICT (id) DO
VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO
UPDATE
SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id,
is_active = EXCLUDED.is_active;
-- name: GetSupportedLeagues :many
SELECT id,
name,
country_code,
bet365_id,
is_active
FROM leagues
WHERE is_active = true;
is_active = EXCLUDED.is_active,
sport_id = EXCLUDED.sport_id;
-- name: GetAllLeagues :many
SELECT id,
name,
country_code,
bet365_id,
is_active
FROM leagues;
is_active,
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
SELECT EXISTS(
SELECT 1
@ -36,11 +44,20 @@ SELECT EXISTS(
);
-- name: UpdateLeague :exec
UPDATE leagues
SET name = $1,
country_code = $2,
bet365_id = $3,
is_active = $4
WHERE id = $5;
SET name = COALESCE(sqlc.narg('name'), name),
country_code = COALESCE(sqlc.narg('country_code'), country_code),
bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id),
is_active = COALESCE(sqlc.narg('is_active'), is_active),
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
UPDATE leagues
SET is_active = $2

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,4 +6,22 @@ type League struct {
CountryCode string `json:"cc" example:"uk"`
Bet365ID int32 `json:"bet365_id" example:"1121"`
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

@ -28,14 +28,14 @@ type Score struct {
}
type CommonResultResponse struct {
ID string `json:"id"`
SportID string `json:"sport_id"`
Time string `json:"time"`
TimeStatus string `json:"time_status"`
League League `json:"league"`
Home Team `json:"home"`
Away Team `json:"away"`
SS string `json:"ss"`
ID string `json:"id"`
SportID string `json:"sport_id"`
Time string `json:"time"`
TimeStatus string `json:"time_status"`
League LeagueRes `json:"league"`
Home Team `json:"home"`
Away Team `json:"away"`
SS string `json:"ss"`
}
type FootballResultResponse struct {

View File

@ -153,6 +153,10 @@ func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.Ev
Time: filter.LastStartTime.Value.UTC(),
Valid: filter.LastStartTime.Valid,
},
CountryCode: pgtype.Text{
String: filter.CountryCode.Value,
Valid: filter.CountryCode.Valid,
},
})
if err != nil {
@ -195,6 +199,10 @@ func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.Ev
Time: filter.LastStartTime.Value.UTC(),
Valid: filter.LastStartTime.Valid,
},
CountryCode: pgtype.Text{
String: filter.CountryCode.Value,
Valid: filter.CountryCode.Valid,
},
})
if err != nil {
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},
Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true},
IsActive: pgtype.Bool{Bool: l.IsActive, Valid: true},
SportID: l.SportID,
})
}
func (s *Store) GetSupportedLeagues(ctx context.Context) ([]domain.League, error) {
leagues, err := s.queries.GetSupportedLeagues(ctx)
if err != nil {
return nil, err
}
supportedLeagues := make([]domain.League, len(leagues))
for i, league := range leagues {
supportedLeagues[i] = domain.League{
ID: league.ID,
Name: league.Name,
CountryCode: league.CountryCode.String,
Bet365ID: league.Bet365ID.Int32,
IsActive: league.IsActive.Bool,
}
}
return supportedLeagues, nil
}
func (s *Store) GetAllLeagues(ctx context.Context) ([]domain.League, error) {
l, err := s.queries.GetAllLeagues(ctx)
func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error) {
l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{
CountryCode: pgtype.Text{
String: filter.CountryCode.Value,
Valid: filter.CountryCode.Valid,
},
SportID: pgtype.Int4{
Int32: filter.SportID.Value,
Valid: filter.SportID.Valid,
},
IsActive: pgtype.Bool{
Bool: filter.IsActive.Value,
Valid: filter.IsActive.Valid,
},
Limit: pgtype.Int4{
Int32: int32(filter.Limit.Value),
Valid: filter.Limit.Valid,
},
Offset: pgtype.Int4{
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
})
if err != nil {
return nil, err
}
@ -51,6 +54,7 @@ func (s *Store) GetAllLeagues(ctx context.Context) ([]domain.League, error) {
CountryCode: league.CountryCode.String,
Bet365ID: league.Bet365ID.Int32,
IsActive: league.IsActive.Bool,
SportID: league.SportID,
}
}
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) SetLeagueInActive(ctx context.Context, l domain.League) error {
return s.queries.UpdateLeague(ctx, dbgen.UpdateLeagueParams{
Name: l.Name,
CountryCode: pgtype.Text{String: l.CountryCode, Valid: true},
Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true},
IsActive: pgtype.Bool{Bool: false, Valid: true},
func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {
err := s.queries.UpdateLeague(ctx, dbgen.UpdateLeagueParams{
ID: league.ID,
Name: pgtype.Text{
String: league.Name.Value,
Valid: league.Name.Valid,
},
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 (
"context"
"encoding/json"
"os"
"strconv"
"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)
// return
// }
for _, sportID := range sportIDs {
for sportIndex, sportID := range sportIDs {
var totalPages int = 1
var page int = 0
var limit int = 100
var limit int = 200
var count int = 0
log.Printf("Sport ID %d", sportID)
for page <= totalPages {
page = page + 1
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)
if err != nil {
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
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,
Name: ev.League.Name,
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 {
log.Printf("Skipping league %v", ev.League.Name)
skippedLeague = append(skippedLeague, ev.League.Name)
continue
}

View File

@ -7,6 +7,8 @@ import (
)
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
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) {
return s.store.GetAllLeagues(ctx)
func (s *service) SaveLeague(ctx context.Context, l domain.League) error {
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 {
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(1)
go func() {
defer wg.Done()
if err := s.fetchBet365Odds(ctx); err != nil {
errChan <- fmt.Errorf("bet365 odds fetching error: %w", err)
}
}()
// go func() {
// defer wg.Done()
// if err := s.fetchBet365Odds(ctx); err != nil {
// errChan <- fmt.Errorf("bet365 odds fetching error: %w", err)
// if err := s.fetchBwinOdds(ctx); err != nil {
// errChan <- fmt.Errorf("bwin odds fetching error: %w", err)
// }
// }()
go func() {
defer wg.Done()
if err := s.fetchBwinOdds(ctx); err != nil {
errChan <- fmt.Errorf("bwin odds fetching error: %w", err)
}
wg.Wait()
close(errChan)
}()
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 {
@ -208,7 +217,6 @@ func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error {
return nil
}
func (s *ServiceImpl) FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error) {
eventID, err := strconv.ParseInt(eventIDStr, 10, 64)

View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"log"
"log/slog"
"net/http"
"strconv"
@ -15,28 +16,31 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
)
type Service struct {
repo *repository.Store
config *config.Config
logger *slog.Logger
client *http.Client
betSvc bet.Service
oddSvc odds.ServiceImpl
eventSvc event.Service
repo *repository.Store
config *config.Config
logger *slog.Logger
client *http.Client
betSvc bet.Service
oddSvc odds.ServiceImpl
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{
repo: repo,
config: cfg,
logger: logger,
client: &http.Client{Timeout: 10 * time.Second},
betSvc: betSvc,
oddSvc: oddSvc,
eventSvc: eventSvc,
repo: repo,
config: cfg,
logger: logger,
client: &http.Client{Timeout: 10 * time.Second},
betSvc: betSvc,
oddSvc: oddSvc,
eventSvc: eventSvc,
leagueSvc: leagueSvc,
}
}
@ -274,6 +278,33 @@ func (s *Service) CheckAndUpdateExpiredEvents(ctx context.Context) (int64, error
}
updated++
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 {

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

View File

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

View File

@ -1,19 +1,77 @@
package handlers
import (
"fmt"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"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 {
leagues, err := h.leagueSvc.GetAllLeagues(c.Context())
if err != nil {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get leagues", err, nil)
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10)
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 {
@ -21,6 +79,7 @@ type SetLeagueActiveReq struct {
}
func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
fmt.Printf("Set Active Leagues")
leagueIdStr := c.Params("id")
if leagueIdStr == "" {
response.WriteJSON(c, fiber.StatusBadRequest, "Missing league id", nil, nil)

View File

@ -1,6 +1,7 @@
package handlers
import (
"fmt"
"strconv"
"time"
@ -9,33 +10,6 @@ import (
"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
// @Summary Retrieve all prematch odds
// @Description Retrieve all prematch odds from the database
@ -44,7 +18,7 @@ func (h *Handler) GetPrematchOdds(c *fiber.Ctx) error {
// @Produce json
// @Success 200 {array} domain.Odd
// @Failure 500 {object} response.APIResponse
// @Router /prematch/odds [get]
// @Router /odds [get]
func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error {
odds, err := h.prematchSvc.GetALLPrematchOdds(c.Context())
@ -67,7 +41,7 @@ func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error {
// @Success 200 {array} domain.RawOddsByMarketID
// @Failure 400 {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 {
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)
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)
}
@ -99,20 +74,25 @@ func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error {
// @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.UpcomingEvent
// @Failure 500 {object} response.APIResponse
// @Router /prematch/events [get]
// @Router /events [get]
func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
page := c.QueryInt("page", 1)
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")
sportIDQuery := c.Query("sport_id")
firstStartTimeQuery := c.Query("first_start_time")
lastStartTimeQuery := c.Query("last_start_time")
var leagueID domain.ValidInt32
if leagueIDQuery != "" {
leagueIDInt, err := strconv.Atoi(leagueIDQuery)
@ -125,6 +105,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
Valid: true,
}
}
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
@ -137,7 +118,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
Valid: true,
}
}
firstStartTimeQuery := c.Query("first_start_time")
var firstStartTime domain.ValidTime
if firstStartTimeQuery != "" {
firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
@ -150,6 +131,8 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
Valid: true,
}
}
lastStartTimeQuery := c.Query("last_start_time")
var lastStartTime domain.ValidTime
if lastStartTimeQuery != "" {
lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery)
@ -163,15 +146,11 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
}
}
limit := domain.ValidInt64{
Value: int64(pageSize),
Valid: true,
countryCodeQuery := c.Query("cc")
countryCode := domain.ValidString{
Value: countryCodeQuery,
Valid: countryCodeQuery != "",
}
offset := domain.ValidInt64{
Value: int64(page - 1),
Valid: true,
}
events, total, err := h.eventSvc.GetPaginatedUpcomingEvents(
c.Context(), domain.EventFilter{
SportID: sportID,
@ -180,6 +159,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
LastStartTime: lastStartTime,
Limit: limit,
Offset: offset,
CountryCode: countryCode,
})
// fmt.Printf("League ID: %v", leagueID)
@ -201,7 +181,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
// @Success 200 {object} domain.UpcomingEvent
// @Failure 400 {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 {
id := c.Params("id")
@ -229,8 +209,8 @@ func (h *Handler) GetUpcomingEventByID(c *fiber.Ctx) error {
// @Success 200 {array} domain.Odd
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /prematch/odds/upcoming/{upcoming_id} [get]
func (h *Handler) GetPrematchOddsByUpcomingID(c *fiber.Ctx) error {
// @Router /odds/upcoming/{upcoming_id} [get]
func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error {
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
@ -269,7 +249,7 @@ type UpdateEventStatusReq struct {
// @Success 200 {object} response.APIResponse
// @Failure 400 {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 {
eventID := c.Params("id")
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.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/upcoming/:upcoming_id", h.GetOddsByUpcomingID)
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/:id", h.GetUpcomingEventByID)