fix: outcome and league optimization
This commit is contained in:
parent
76518fcbd1
commit
1c7ae8232c
62
.env
62
.env
|
|
@ -1,62 +0,0 @@
|
||||||
# REPORT_EXPORT_PATH="C:\\ProgramData\\FortuneBet\\exported_reports" #prod env
|
|
||||||
REPORT_EXPORT_PATH ="./exported_reports" #dev env
|
|
||||||
|
|
||||||
RESEND_SENDER_EMAIL=customer@fortunebets.net
|
|
||||||
RESEND_API_KEY=re_GSTRa9Pp_JkRWBpST9MvaCVULJF8ybGKE
|
|
||||||
|
|
||||||
ENV=development
|
|
||||||
PORT=8080
|
|
||||||
DB_URL=postgresql://root:secret@localhost:5422/gh?sslmode=disable
|
|
||||||
REFRESH_EXPIRY=2592000
|
|
||||||
JWT_KEY=mysecretkey
|
|
||||||
ACCESS_EXPIRY=600
|
|
||||||
LOG_LEVEL=debug
|
|
||||||
AFRO_SMS_API_KEY=eyJhbGciOiJIUzI1NiJ9.eyJpZGVudGlmaWVyIjoiQlR5ZDFIYmJFYXZ6YUo3dzZGell1RUlieGozSElJeTYiLCJleHAiOjE4OTYwMTM5MTksImlhdCI6MTczODI0NzUxOSwianRpIjoiOWIyNTJkNWQtODcxOC00NGYzLWIzMDQtMGYxOTRhY2NiNTU3In0.XPw8s6mCx1Tp1CfxGmjFRROmdkVnghnqfmsniB-Ze8I
|
|
||||||
AFRO_SMS_SENDER_NAME=FortuneBets
|
|
||||||
|
|
||||||
AFRO_SMS_RECEIVER_PHONE_NUMBER=
|
|
||||||
BET365_TOKEN=158046-hesJDP2Cay2M5G
|
|
||||||
POPOK_CLIENT_ID=1
|
|
||||||
POPOK_PLATFORM=111
|
|
||||||
POPOK_SECRET_KEY=XwFQ76Y59zBxGryh
|
|
||||||
# POPOK_BASE_URL=https://api.pokgaming.com/game/launch #Production
|
|
||||||
# POPOK_BASE_URL=https://games.pokgaming.com/launch #Production
|
|
||||||
# POPOK_BASE_URL=https://sandbox.pokgaming.com/game/launch #Staging
|
|
||||||
# POPOK_BASE_URL=https://test-api.pokgaming.com/launch #Staging
|
|
||||||
POPOK_BASE_URL=https://st.pokgaming.com/ #Staging
|
|
||||||
|
|
||||||
POPOK_CALLBACK_URL=1
|
|
||||||
|
|
||||||
#Muli-currency Support
|
|
||||||
FIXER_API_KEY=3b0f1eb30d-63c875026d-sxy9pl
|
|
||||||
BASE_CURRENCY=ETB
|
|
||||||
FIXER_BASE_URL=https://api.apilayer.com/fixer
|
|
||||||
|
|
||||||
# Chapa API Configuration
|
|
||||||
CHAPA_TRANSFER_TYPE="Payout"
|
|
||||||
CHAPA_PAYMENT_TYPE="API"
|
|
||||||
CHAPA_BASE_URL="https://api.chapa.co/v1"
|
|
||||||
CHAPA_ENCRYPTION_KEY=zLdYrjnBCknMvFikmP5jBfen
|
|
||||||
CHAPA_PUBLIC_KEY=CHAPUBK_TEST-HJR0qhQRPLTkauNy9Q8UrmskPTOR31aC
|
|
||||||
CHAPA_SECRET_KEY=CHASECK_TEST-q3jypwmFK6XJGYOK3aX4z9Kogd9KaHhF
|
|
||||||
CHAPA_CALLBACK_URL="https://fortunebet.com/api/v1/payments/callback" # Optional
|
|
||||||
CHAPA_RETURN_URL="https://fortunebet.com/api/v1/payment-success" # Optional
|
|
||||||
|
|
||||||
#Alea Play
|
|
||||||
ALEA_ENABLED=true
|
|
||||||
ALEA_BASE_URL=https://api.aleaplay.com
|
|
||||||
ALEA_OPERATOR_ID=operator_id
|
|
||||||
ALEA_SECRET_KEY=hmac_secret
|
|
||||||
ALEA_GAME_LIST_URL=https://api.aleaplay.com/games/list # Optional
|
|
||||||
ALEA_DEFAULT_CURRENCY=USD # Optional (default: USD)
|
|
||||||
ALEA_SESSION_TIMEOUT=24 # Optional (hours, default: 24)
|
|
||||||
ALEA_GAME_ID_AVIATOR=aviator_prod
|
|
||||||
|
|
||||||
|
|
||||||
# Veli Games
|
|
||||||
VELI_ENABLED=true
|
|
||||||
VELI_API_URL=https://api.velitech.games
|
|
||||||
VELI_OPERATOR_KEY=Veli123
|
|
||||||
VELI_SECRET_KEY=hmac_secret
|
|
||||||
VELI_GAME_ID_AVIATOR=veli_aviator_v1
|
|
||||||
VELI_DEFAULT_CURRENCY=USD
|
|
||||||
|
|
@ -122,7 +122,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, domain.MongoDBLogger)
|
betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, logger, domain.MongoDBLogger)
|
||||||
resultSvc := result.NewService(store, cfg, logger, *betSvc, *oddsSvc, eventSvc, leagueSvc)
|
resultSvc := result.NewService(store, cfg, logger, *betSvc, *oddsSvc, eventSvc, leagueSvc, notificationSvc)
|
||||||
referalRepo := repository.NewReferralRepository(store)
|
referalRepo := repository.NewReferralRepository(store)
|
||||||
vitualGameRepo := repository.NewVirtualGameRepository(store)
|
vitualGameRepo := repository.NewVirtualGameRepository(store)
|
||||||
recommendationRepo := repository.NewRecommendationRepository(store)
|
recommendationRepo := repository.NewRecommendationRepository(store)
|
||||||
|
|
|
||||||
|
|
@ -250,10 +250,12 @@ CREATE TABLE companies (
|
||||||
CREATE TABLE leagues (
|
CREATE TABLE leagues (
|
||||||
id BIGINT PRIMARY KEY,
|
id BIGINT PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
|
img TEXT,
|
||||||
country_code TEXT,
|
country_code TEXT,
|
||||||
bet365_id INT,
|
bet365_id INT,
|
||||||
sport_id INT NOT NULL,
|
sport_id INT NOT NULL,
|
||||||
is_active BOOLEAN DEFAULT true
|
is_active BOOLEAN DEFAULT true,
|
||||||
|
is_featured BOOLEAN DEFAULT false
|
||||||
);
|
);
|
||||||
CREATE TABLE teams (
|
CREATE TABLE teams (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,11 @@ UPDATE bet_outcomes
|
||||||
SET status = $1
|
SET status = $1
|
||||||
WHERE id = $2
|
WHERE id = $2
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
-- name: UpdateBetOutcomeStatusForEvent :many
|
||||||
|
UPDATE bet_outcomes
|
||||||
|
SEt status = $1
|
||||||
|
WHERE event_id = $2
|
||||||
|
RETURNING *;
|
||||||
-- name: UpdateStatus :exec
|
-- name: UpdateStatus :exec
|
||||||
UPDATE bets
|
UPDATE bets
|
||||||
SET status = $1,
|
SET status = $1,
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,16 @@ INSERT INTO leagues (
|
||||||
country_code,
|
country_code,
|
||||||
bet365_id,
|
bet365_id,
|
||||||
sport_id,
|
sport_id,
|
||||||
is_active
|
is_active,
|
||||||
|
is_featured
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO
|
VALUES ($1, $2, $3, $4, $5, $6, $7) 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,
|
||||||
|
is_featured = EXCLUDED.is_featured,
|
||||||
sport_id = EXCLUDED.sport_id;
|
sport_id = EXCLUDED.sport_id;
|
||||||
-- name: GetAllLeagues :many
|
-- name: GetAllLeagues :many
|
||||||
SELECT id,
|
SELECT id,
|
||||||
|
|
@ -20,6 +22,7 @@ SELECT id,
|
||||||
country_code,
|
country_code,
|
||||||
bet365_id,
|
bet365_id,
|
||||||
is_active,
|
is_active,
|
||||||
|
is_featured,
|
||||||
sport_id
|
sport_id
|
||||||
FROM leagues
|
FROM leagues
|
||||||
WHERE (
|
WHERE (
|
||||||
|
|
@ -34,7 +37,21 @@ WHERE (
|
||||||
is_active = sqlc.narg('is_active')
|
is_active = sqlc.narg('is_active')
|
||||||
OR sqlc.narg('is_active') IS NULL
|
OR sqlc.narg('is_active') IS NULL
|
||||||
)
|
)
|
||||||
|
AND (
|
||||||
|
is_featured = sqlc.narg('is_featured')
|
||||||
|
OR sqlc.narg('is_featured') IS NULL
|
||||||
|
)
|
||||||
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
|
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
|
||||||
|
-- name: GetFeaturedLeagues :many
|
||||||
|
SELECT id,
|
||||||
|
name,
|
||||||
|
country_code,
|
||||||
|
bet365_id,
|
||||||
|
is_active,
|
||||||
|
is_featured,
|
||||||
|
sport_id
|
||||||
|
FROM leagues
|
||||||
|
WHERE is_featured = true;
|
||||||
-- name: CheckLeagueSupport :one
|
-- name: CheckLeagueSupport :one
|
||||||
SELECT EXISTS(
|
SELECT EXISTS(
|
||||||
SELECT 1
|
SELECT 1
|
||||||
|
|
@ -48,6 +65,7 @@ SET name = COALESCE(sqlc.narg('name'), name),
|
||||||
country_code = COALESCE(sqlc.narg('country_code'), country_code),
|
country_code = COALESCE(sqlc.narg('country_code'), country_code),
|
||||||
bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id),
|
bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id),
|
||||||
is_active = COALESCE(sqlc.narg('is_active'), is_active),
|
is_active = COALESCE(sqlc.narg('is_active'), is_active),
|
||||||
|
is_featured = COALESCE(sqlc.narg('is_featured'), is_featured),
|
||||||
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
|
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
-- name: UpdateLeagueByBet365ID :exec
|
-- name: UpdateLeagueByBet365ID :exec
|
||||||
|
|
@ -56,6 +74,7 @@ SET name = COALESCE(sqlc.narg('name'), name),
|
||||||
id = COALESCE(sqlc.narg('id'), id),
|
id = COALESCE(sqlc.narg('id'), id),
|
||||||
country_code = COALESCE(sqlc.narg('country_code'), country_code),
|
country_code = COALESCE(sqlc.narg('country_code'), country_code),
|
||||||
is_active = COALESCE(sqlc.narg('is_active'), is_active),
|
is_active = COALESCE(sqlc.narg('is_active'), is_active),
|
||||||
|
is_featured = COALESCE(sqlc.narg('is_featured'), is_featured),
|
||||||
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
|
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
|
||||||
WHERE bet365_id = $1;
|
WHERE bet365_id = $1;
|
||||||
-- name: SetLeagueActive :exec
|
-- name: SetLeagueActive :exec
|
||||||
|
|
|
||||||
|
|
@ -468,6 +468,54 @@ func (q *Queries) UpdateBetOutcomeStatus(ctx context.Context, arg UpdateBetOutco
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UpdateBetOutcomeStatusForEvent = `-- name: UpdateBetOutcomeStatusForEvent :many
|
||||||
|
UPDATE bet_outcomes
|
||||||
|
SEt status = $1
|
||||||
|
WHERE event_id = $2
|
||||||
|
RETURNING id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateBetOutcomeStatusForEventParams struct {
|
||||||
|
Status int32 `json:"status"`
|
||||||
|
EventID int64 `json:"event_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateBetOutcomeStatusForEvent(ctx context.Context, arg UpdateBetOutcomeStatusForEventParams) ([]BetOutcome, error) {
|
||||||
|
rows, err := q.db.Query(ctx, UpdateBetOutcomeStatusForEvent, arg.Status, arg.EventID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []BetOutcome
|
||||||
|
for rows.Next() {
|
||||||
|
var i BetOutcome
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.BetID,
|
||||||
|
&i.SportID,
|
||||||
|
&i.EventID,
|
||||||
|
&i.OddID,
|
||||||
|
&i.HomeTeamName,
|
||||||
|
&i.AwayTeamName,
|
||||||
|
&i.MarketID,
|
||||||
|
&i.MarketName,
|
||||||
|
&i.Odd,
|
||||||
|
&i.OddName,
|
||||||
|
&i.OddHeader,
|
||||||
|
&i.OddHandicap,
|
||||||
|
&i.Status,
|
||||||
|
&i.Expires,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const UpdateCashOut = `-- name: UpdateCashOut :exec
|
const UpdateCashOut = `-- name: UpdateCashOut :exec
|
||||||
UPDATE bets
|
UPDATE bets
|
||||||
SET cashed_out = $2,
|
SET cashed_out = $2,
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ SELECT id,
|
||||||
country_code,
|
country_code,
|
||||||
bet365_id,
|
bet365_id,
|
||||||
is_active,
|
is_active,
|
||||||
|
is_featured,
|
||||||
sport_id
|
sport_id
|
||||||
FROM leagues
|
FROM leagues
|
||||||
WHERE (
|
WHERE (
|
||||||
|
|
@ -47,13 +48,18 @@ WHERE (
|
||||||
is_active = $3
|
is_active = $3
|
||||||
OR $3 IS NULL
|
OR $3 IS NULL
|
||||||
)
|
)
|
||||||
LIMIT $5 OFFSET $4
|
AND (
|
||||||
|
is_featured = $4
|
||||||
|
OR $4 IS NULL
|
||||||
|
)
|
||||||
|
LIMIT $6 OFFSET $5
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetAllLeaguesParams struct {
|
type GetAllLeaguesParams struct {
|
||||||
CountryCode pgtype.Text `json:"country_code"`
|
CountryCode pgtype.Text `json:"country_code"`
|
||||||
SportID pgtype.Int4 `json:"sport_id"`
|
SportID pgtype.Int4 `json:"sport_id"`
|
||||||
IsActive pgtype.Bool `json:"is_active"`
|
IsActive pgtype.Bool `json:"is_active"`
|
||||||
|
IsFeatured pgtype.Bool `json:"is_featured"`
|
||||||
Offset pgtype.Int4 `json:"offset"`
|
Offset pgtype.Int4 `json:"offset"`
|
||||||
Limit pgtype.Int4 `json:"limit"`
|
Limit pgtype.Int4 `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
@ -64,6 +70,7 @@ type GetAllLeaguesRow struct {
|
||||||
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"`
|
||||||
|
IsFeatured pgtype.Bool `json:"is_featured"`
|
||||||
SportID int32 `json:"sport_id"`
|
SportID int32 `json:"sport_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,6 +79,7 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
|
||||||
arg.CountryCode,
|
arg.CountryCode,
|
||||||
arg.SportID,
|
arg.SportID,
|
||||||
arg.IsActive,
|
arg.IsActive,
|
||||||
|
arg.IsFeatured,
|
||||||
arg.Offset,
|
arg.Offset,
|
||||||
arg.Limit,
|
arg.Limit,
|
||||||
)
|
)
|
||||||
|
|
@ -88,6 +96,57 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
|
||||||
&i.CountryCode,
|
&i.CountryCode,
|
||||||
&i.Bet365ID,
|
&i.Bet365ID,
|
||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
|
&i.IsFeatured,
|
||||||
|
&i.SportID,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetFeaturedLeagues = `-- name: GetFeaturedLeagues :many
|
||||||
|
SELECT id,
|
||||||
|
name,
|
||||||
|
country_code,
|
||||||
|
bet365_id,
|
||||||
|
is_active,
|
||||||
|
is_featured,
|
||||||
|
sport_id
|
||||||
|
FROM leagues
|
||||||
|
WHERE is_featured = true
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetFeaturedLeaguesRow 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"`
|
||||||
|
IsFeatured pgtype.Bool `json:"is_featured"`
|
||||||
|
SportID int32 `json:"sport_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetFeaturedLeagues(ctx context.Context) ([]GetFeaturedLeaguesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetFeaturedLeagues)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetFeaturedLeaguesRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetFeaturedLeaguesRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.CountryCode,
|
||||||
|
&i.Bet365ID,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.IsFeatured,
|
||||||
&i.SportID,
|
&i.SportID,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -107,14 +166,16 @@ INSERT INTO leagues (
|
||||||
country_code,
|
country_code,
|
||||||
bet365_id,
|
bet365_id,
|
||||||
sport_id,
|
sport_id,
|
||||||
is_active
|
is_active,
|
||||||
|
is_featured
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO
|
VALUES ($1, $2, $3, $4, $5, $6, $7) 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,
|
||||||
|
is_featured = EXCLUDED.is_featured,
|
||||||
sport_id = EXCLUDED.sport_id
|
sport_id = EXCLUDED.sport_id
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -125,6 +186,7 @@ type InsertLeagueParams struct {
|
||||||
Bet365ID pgtype.Int4 `json:"bet365_id"`
|
Bet365ID pgtype.Int4 `json:"bet365_id"`
|
||||||
SportID int32 `json:"sport_id"`
|
SportID int32 `json:"sport_id"`
|
||||||
IsActive pgtype.Bool `json:"is_active"`
|
IsActive pgtype.Bool `json:"is_active"`
|
||||||
|
IsFeatured pgtype.Bool `json:"is_featured"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) error {
|
func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) error {
|
||||||
|
|
@ -135,6 +197,7 @@ func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) erro
|
||||||
arg.Bet365ID,
|
arg.Bet365ID,
|
||||||
arg.SportID,
|
arg.SportID,
|
||||||
arg.IsActive,
|
arg.IsActive,
|
||||||
|
arg.IsFeatured,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -161,7 +224,8 @@ SET name = COALESCE($2, name),
|
||||||
country_code = COALESCE($3, country_code),
|
country_code = COALESCE($3, country_code),
|
||||||
bet365_id = COALESCE($4, bet365_id),
|
bet365_id = COALESCE($4, bet365_id),
|
||||||
is_active = COALESCE($5, is_active),
|
is_active = COALESCE($5, is_active),
|
||||||
sport_id = COALESCE($6, sport_id)
|
is_featured = COALESCE($6, is_featured),
|
||||||
|
sport_id = COALESCE($7, sport_id)
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -171,6 +235,7 @@ type UpdateLeagueParams struct {
|
||||||
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"`
|
||||||
|
IsFeatured pgtype.Bool `json:"is_featured"`
|
||||||
SportID pgtype.Int4 `json:"sport_id"`
|
SportID pgtype.Int4 `json:"sport_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,6 +246,7 @@ func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) erro
|
||||||
arg.CountryCode,
|
arg.CountryCode,
|
||||||
arg.Bet365ID,
|
arg.Bet365ID,
|
||||||
arg.IsActive,
|
arg.IsActive,
|
||||||
|
arg.IsFeatured,
|
||||||
arg.SportID,
|
arg.SportID,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
|
|
@ -192,7 +258,8 @@ SET name = COALESCE($2, name),
|
||||||
id = COALESCE($3, id),
|
id = COALESCE($3, id),
|
||||||
country_code = COALESCE($4, country_code),
|
country_code = COALESCE($4, country_code),
|
||||||
is_active = COALESCE($5, is_active),
|
is_active = COALESCE($5, is_active),
|
||||||
sport_id = COALESCE($6, sport_id)
|
is_featured = COALESCE($6, is_featured),
|
||||||
|
sport_id = COALESCE($7, sport_id)
|
||||||
WHERE bet365_id = $1
|
WHERE bet365_id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -202,6 +269,7 @@ type UpdateLeagueByBet365IDParams struct {
|
||||||
ID pgtype.Int8 `json:"id"`
|
ID pgtype.Int8 `json:"id"`
|
||||||
CountryCode pgtype.Text `json:"country_code"`
|
CountryCode pgtype.Text `json:"country_code"`
|
||||||
IsActive pgtype.Bool `json:"is_active"`
|
IsActive pgtype.Bool `json:"is_active"`
|
||||||
|
IsFeatured pgtype.Bool `json:"is_featured"`
|
||||||
SportID pgtype.Int4 `json:"sport_id"`
|
SportID pgtype.Int4 `json:"sport_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,6 +280,7 @@ func (q *Queries) UpdateLeagueByBet365ID(ctx context.Context, arg UpdateLeagueBy
|
||||||
arg.ID,
|
arg.ID,
|
||||||
arg.CountryCode,
|
arg.CountryCode,
|
||||||
arg.IsActive,
|
arg.IsActive,
|
||||||
|
arg.IsFeatured,
|
||||||
arg.SportID,
|
arg.SportID,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -218,10 +218,12 @@ type ExchangeRate struct {
|
||||||
type League struct {
|
type League struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Img pgtype.Text `json:"img"`
|
||||||
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"`
|
SportID int32 `json:"sport_id"`
|
||||||
IsActive pgtype.Bool `json:"is_active"`
|
IsActive pgtype.Bool `json:"is_active"`
|
||||||
|
IsFeatured pgtype.Bool `json:"is_featured"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Notification struct {
|
type Notification struct {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ type League struct {
|
||||||
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"`
|
SportID int32 `json:"sport_id" example:"1"`
|
||||||
|
IsFeatured bool `json:"is_featured" example:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateLeague struct {
|
type UpdateLeague struct {
|
||||||
|
|
@ -15,6 +16,7 @@ type UpdateLeague struct {
|
||||||
CountryCode ValidString `json:"cc" example:"uk"`
|
CountryCode ValidString `json:"cc" example:"uk"`
|
||||||
Bet365ID ValidInt32 `json:"bet365_id" example:"1121"`
|
Bet365ID ValidInt32 `json:"bet365_id" example:"1121"`
|
||||||
IsActive ValidBool `json:"is_active" example:"false"`
|
IsActive ValidBool `json:"is_active" example:"false"`
|
||||||
|
IsFeatured ValidBool `json:"is_featured" example:"false"`
|
||||||
SportID ValidInt32 `json:"sport_id" example:"1"`
|
SportID ValidInt32 `json:"sport_id" example:"1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,6 +24,69 @@ type LeagueFilter struct {
|
||||||
CountryCode ValidString
|
CountryCode ValidString
|
||||||
SportID ValidInt32
|
SportID ValidInt32
|
||||||
IsActive ValidBool
|
IsActive ValidBool
|
||||||
|
IsFeatured ValidBool
|
||||||
Limit ValidInt64
|
Limit ValidInt64
|
||||||
Offset ValidInt64
|
Offset ValidInt64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// These leagues are automatically featured when the league is created
|
||||||
|
var FeaturedLeagues = []int64{
|
||||||
|
// Football
|
||||||
|
10044469, // Ethiopian Premier League
|
||||||
|
10041282, //Premier League
|
||||||
|
10083364, //La Liga
|
||||||
|
10041095, //German Bundesliga
|
||||||
|
10041100, //Ligue 1
|
||||||
|
10041809, //UEFA Champions League
|
||||||
|
10041957, //UEFA Europa League
|
||||||
|
10079560, //UEFA Conference League
|
||||||
|
10050282, //UEFA Nations League
|
||||||
|
10044685, //FIFA Club World Cup
|
||||||
|
10050346, //UEFA Super Cup
|
||||||
|
10081269, //CONCACAF Champions Cup
|
||||||
|
10070189, //CONCACAF Gold Cup
|
||||||
|
|
||||||
|
10067913, //Europe - World Cup Qualifying
|
||||||
|
10040162, //Asia - World Cup Qualifying
|
||||||
|
10067624, //South America - World Cup Qualifying
|
||||||
|
10073057, //North & Central America - World Cup Qualifying
|
||||||
|
|
||||||
|
10037075, //International Match
|
||||||
|
10077480, //Women’s International
|
||||||
|
10037109, //Europe Friendlies
|
||||||
|
10068837, //Euro U21
|
||||||
|
|
||||||
|
10041315, //Italian Serie A
|
||||||
|
10036538, //Spain Segunda
|
||||||
|
10047168, // US MLS
|
||||||
|
|
||||||
|
10043156, //England FA Cup
|
||||||
|
10042103, //France Cup
|
||||||
|
10041088, //Premier League 2
|
||||||
|
10084250, //Turkiye Super League
|
||||||
|
10041187, //Kenya Super League
|
||||||
|
10041391, //Netherlands Eredivisie
|
||||||
|
|
||||||
|
// Basketball
|
||||||
|
10041830, //NBA
|
||||||
|
10049984, //WNBA
|
||||||
|
10037165, //German Bundesliga
|
||||||
|
10036608, //Italian Lega 1
|
||||||
|
10040795, //EuroLeague
|
||||||
|
10041534, //Basketball Africa League
|
||||||
|
|
||||||
|
// Ice Hockey
|
||||||
|
10037477, //NHL
|
||||||
|
10037447, //AHL
|
||||||
|
10069385, //IIHF World Championship
|
||||||
|
|
||||||
|
// AMERICAN FOOTBALL
|
||||||
|
10037219, //NFL
|
||||||
|
|
||||||
|
// BASEBALL
|
||||||
|
10037485, // MLB
|
||||||
|
|
||||||
|
// VOLLEYBALL
|
||||||
|
10069666, //FIVB Nations League
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,9 @@ const (
|
||||||
NOTIFICATION_TYPE_TRANSFER_FAIL NotificationType = "transfer_failed"
|
NOTIFICATION_TYPE_TRANSFER_FAIL NotificationType = "transfer_failed"
|
||||||
NOTIFICATION_TYPE_TRANSFER_SUCCESS NotificationType = "transfer_success"
|
NOTIFICATION_TYPE_TRANSFER_SUCCESS NotificationType = "transfer_success"
|
||||||
NOTIFICATION_TYPE_ADMIN_ALERT NotificationType = "admin_alert"
|
NOTIFICATION_TYPE_ADMIN_ALERT NotificationType = "admin_alert"
|
||||||
NOTIFICATION_RECEIVER_ADMIN NotificationRecieverSide = "admin"
|
NOTIFICATION_TYPE_BET_RESULT NotificationType = "bet_result"
|
||||||
|
|
||||||
|
NOTIFICATION_RECEIVER_ADMIN NotificationRecieverSide = "admin"
|
||||||
NotificationRecieverSideAdmin NotificationRecieverSide = "admin"
|
NotificationRecieverSideAdmin NotificationRecieverSide = "admin"
|
||||||
NotificationRecieverSideCustomer NotificationRecieverSide = "customer"
|
NotificationRecieverSideCustomer NotificationRecieverSide = "customer"
|
||||||
NotificationRecieverSideCashier NotificationRecieverSide = "cashier"
|
NotificationRecieverSideCashier NotificationRecieverSide = "cashier"
|
||||||
|
|
|
||||||
|
|
@ -364,6 +364,28 @@ func (s *Store) UpdateBetOutcomeStatus(ctx context.Context, id int64, status dom
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) {
|
||||||
|
outcomes, err := s.queries.UpdateBetOutcomeStatusForEvent(ctx, dbgen.UpdateBetOutcomeStatusForEventParams{
|
||||||
|
EventID: eventID,
|
||||||
|
Status: int32(status),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
domain.MongoDBLogger.Error("failed to update bet outcome status for event",
|
||||||
|
zap.Int64("eventID", eventID),
|
||||||
|
zap.Int32("status", int32(status)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes))
|
||||||
|
for _, outcome := range outcomes {
|
||||||
|
result = append(result, convertDBBetOutcomes(outcome))
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) DeleteBet(ctx context.Context, id int64) error {
|
func (s *Store) DeleteBet(ctx context.Context, id int64) error {
|
||||||
return s.queries.DeleteBet(ctx, id)
|
return s.queries.DeleteBet(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ 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},
|
||||||
|
IsFeatured: pgtype.Bool{Bool: l.IsFeatured, Valid: true},
|
||||||
SportID: l.SportID,
|
SportID: l.SportID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -33,6 +34,10 @@ func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) (
|
||||||
Bool: filter.IsActive.Value,
|
Bool: filter.IsActive.Value,
|
||||||
Valid: filter.IsActive.Valid,
|
Valid: filter.IsActive.Valid,
|
||||||
},
|
},
|
||||||
|
IsFeatured: pgtype.Bool{
|
||||||
|
Bool: filter.IsFeatured.Value,
|
||||||
|
Valid: filter.IsFeatured.Valid,
|
||||||
|
},
|
||||||
Limit: pgtype.Int4{
|
Limit: pgtype.Int4{
|
||||||
Int32: int32(filter.Limit.Value),
|
Int32: int32(filter.Limit.Value),
|
||||||
Valid: filter.Limit.Valid,
|
Valid: filter.Limit.Valid,
|
||||||
|
|
@ -54,6 +59,29 @@ func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) (
|
||||||
CountryCode: league.CountryCode.String,
|
CountryCode: league.CountryCode.String,
|
||||||
Bet365ID: league.Bet365ID.Int32,
|
Bet365ID: league.Bet365ID.Int32,
|
||||||
IsActive: league.IsActive.Bool,
|
IsActive: league.IsActive.Bool,
|
||||||
|
IsFeatured: league.IsFeatured.Bool,
|
||||||
|
SportID: league.SportID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return leagues, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetFeaturedLeagues(ctx context.Context) ([]domain.League, error) {
|
||||||
|
l, err := s.queries.GetFeaturedLeagues(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
leagues := make([]domain.League, len(l))
|
||||||
|
for i, league := range l {
|
||||||
|
leagues[i] = domain.League{
|
||||||
|
ID: league.ID,
|
||||||
|
Name: league.Name,
|
||||||
|
CountryCode: league.CountryCode.String,
|
||||||
|
Bet365ID: league.Bet365ID.Int32,
|
||||||
|
IsActive: league.IsActive.Bool,
|
||||||
|
|
||||||
SportID: league.SportID,
|
SportID: league.SportID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -93,6 +121,10 @@ func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) er
|
||||||
Bool: league.IsActive.Value,
|
Bool: league.IsActive.Value,
|
||||||
Valid: league.IsActive.Valid,
|
Valid: league.IsActive.Valid,
|
||||||
},
|
},
|
||||||
|
IsFeatured: pgtype.Bool{
|
||||||
|
Bool: league.IsFeatured.Value,
|
||||||
|
Valid: league.IsActive.Valid,
|
||||||
|
},
|
||||||
SportID: pgtype.Int4{
|
SportID: pgtype.Int4{
|
||||||
Int32: league.SportID.Value,
|
Int32: league.SportID.Value,
|
||||||
Valid: league.SportID.Valid,
|
Valid: league.SportID.Valid,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ type BetStore interface {
|
||||||
UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error
|
UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error
|
||||||
UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error
|
UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error
|
||||||
UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)
|
UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)
|
||||||
|
UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error)
|
||||||
DeleteBet(ctx context.Context, id int64) error
|
DeleteBet(ctx context.Context, id int64) error
|
||||||
|
|
||||||
GetBetSummary(ctx context.Context, filter domain.ReportFilter) (
|
GetBetSummary(ctx context.Context, filter domain.ReportFilter) (
|
||||||
|
|
|
||||||
|
|
@ -819,6 +819,19 @@ func (s *Service) UpdateBetOutcomeStatus(ctx context.Context, id int64, status d
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) {
|
||||||
|
outcomes, err := s.betStore.UpdateBetOutcomeStatusForEvent(ctx, eventID, status)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("failed to update bet outcome status",
|
||||||
|
zap.Int64("eventID", eventID),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outcomes, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) DeleteBet(ctx context.Context, id int64) error {
|
func (s *Service) DeleteBet(ctx context.Context, id int64) error {
|
||||||
return s.betStore.DeleteBet(ctx, id)
|
return s.betStore.DeleteBet(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -202,8 +203,10 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, source string) {
|
func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, source string) {
|
||||||
sportIDs := []int{1, 18, 17, 3, 83, 15, 12, 19, 8, 16, 91}
|
// sportIDs := []int{1, 18, 17, 3, 83, 15, 12, 19, 8, 16, 91}
|
||||||
|
sportIDs := []int{1}
|
||||||
// TODO: Add the league skipping again when we have dynamic leagues
|
// TODO: Add the league skipping again when we have dynamic leagues
|
||||||
|
|
||||||
// b, err := os.OpenFile("logs/skipped_leagues.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
// b, err := os.OpenFile("logs/skipped_leagues.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// log.Printf("❌ Failed to open leagues file %v", err)
|
// log.Printf("❌ Failed to open leagues file %v", err)
|
||||||
|
|
@ -212,7 +215,7 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, sour
|
||||||
for sportIndex, 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 = 200
|
var limit int = 1
|
||||||
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 {
|
||||||
|
|
@ -252,10 +255,12 @@ 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
|
||||||
// no this its fine to keep it here
|
// no this its fine to keep it here
|
||||||
// but change the league id to bet365 id later
|
// but change the league id to bet365 id later
|
||||||
|
//Automatically feature the league if its in the list
|
||||||
err = s.store.SaveLeague(ctx, domain.League{
|
err = s.store.SaveLeague(ctx, domain.League{
|
||||||
ID: leagueID,
|
ID: leagueID,
|
||||||
Name: ev.League.Name,
|
Name: ev.League.Name,
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
IsFeatured: slices.Contains(domain.FeaturedLeagues, leagueID),
|
||||||
SportID: convertInt32(ev.SportID),
|
SportID: convertInt32(ev.SportID),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type Service interface {
|
type Service interface {
|
||||||
SaveLeague(ctx context.Context, l domain.League) error
|
SaveLeague(ctx context.Context, l domain.League) error
|
||||||
GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error)
|
GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error)
|
||||||
|
GetFeaturedLeagues(ctx context.Context) ([]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
|
UpdateLeague(ctx context.Context, league domain.UpdateLeague) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ func (s *service) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter)
|
||||||
return s.store.GetAllLeagues(ctx, filter)
|
return s.store.GetAllLeagues(ctx, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) GetFeaturedLeagues(ctx context.Context) ([]domain.League, error) {
|
||||||
|
return s.store.GetFeaturedLeagues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -17,6 +16,7 @@ import (
|
||||||
"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/league"
|
||||||
|
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,9 +29,10 @@ type Service struct {
|
||||||
oddSvc odds.ServiceImpl
|
oddSvc odds.ServiceImpl
|
||||||
eventSvc event.Service
|
eventSvc event.Service
|
||||||
leagueSvc league.Service
|
leagueSvc league.Service
|
||||||
|
notificationSvc *notificationservice.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 {
|
func NewService(repo *repository.Store, cfg *config.Config, logger *slog.Logger, betSvc bet.Service, oddSvc odds.ServiceImpl, eventSvc event.Service, leagueSvc league.Service, notificationSvc *notificationservice.Service) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
|
|
@ -41,6 +42,7 @@ func NewService(repo *repository.Store, cfg *config.Config, logger *slog.Logger,
|
||||||
oddSvc: oddSvc,
|
oddSvc: oddSvc,
|
||||||
eventSvc: eventSvc,
|
eventSvc: eventSvc,
|
||||||
leagueSvc: leagueSvc,
|
leagueSvc: leagueSvc,
|
||||||
|
notificationSvc: notificationSvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,6 +50,127 @@ var (
|
||||||
ErrEventIsNotActive = fmt.Errorf("event has been cancelled or postponed")
|
ErrEventIsNotActive = fmt.Errorf("event has been cancelled or postponed")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (s *Service) UpdateResultForOutcomes(ctx context.Context, eventID int64, resultRes json.RawMessage, sportID int64) error {
|
||||||
|
// TODO: Optimize this since there could be many outcomes with the same event_id and market_id that could be updated the same time
|
||||||
|
outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get pending bet outcomes", "error", err)
|
||||||
|
return fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(outcomes) == 0 {
|
||||||
|
s.logger.Info("No bets have been placed on event", "event", eventID)
|
||||||
|
}
|
||||||
|
// if len(outcomes) == 0 {
|
||||||
|
// fmt.Printf("🕛 No bets have been placed on event %v (%d/%d) \n", eventID, i+1, len(events))
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("✅ %d bets have been placed on event %v (%d/%d) \n", len(outcomes), event.ID, i+1, len(events))
|
||||||
|
// }
|
||||||
|
|
||||||
|
for _, outcome := range outcomes {
|
||||||
|
if outcome.Expires.After(time.Now()) {
|
||||||
|
s.logger.Warn("Outcome is not expired yet", "event_id", "outcome_id", outcome.ID)
|
||||||
|
return fmt.Errorf("Outcome has not expired yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
parseResult, err := s.parseResult(ctx, resultRes, outcome, sportID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to parse result", "event_id", outcome.EventID, "outcome_id", outcome.ID, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, parseResult.Status)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update bet outcome status", "bet_outcome_id", outcome.ID, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if outcome.Status == domain.OUTCOME_STATUS_ERROR || outcome.Status == domain.OUTCOME_STATUS_PENDING {
|
||||||
|
s.logger.Error("Outcome is pending or error", "event_id", outcome.EventID, "outcome_id", outcome.ID)
|
||||||
|
return fmt.Errorf("Error while updating outcome")
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID)
|
||||||
|
if err != nil {
|
||||||
|
if err != bet.ErrOutcomesNotCompleted {
|
||||||
|
s.logger.Error("Failed to check bet outcome for bet", "event_id", outcome.EventID, "error", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.logger.Info("Updating bet status", outcome.BetID, "status:", status.String())
|
||||||
|
err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update bet status", "event id", outcome.EventID, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) RefundAllOutcomes(ctx context.Context, eventID int64) error {
|
||||||
|
outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get pending bet outcomes", "error", err)
|
||||||
|
return fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(outcomes) == 0 {
|
||||||
|
s.logger.Info("No bets have been placed on event", "event", eventID)
|
||||||
|
}
|
||||||
|
|
||||||
|
outcomes, err = s.betSvc.UpdateBetOutcomeStatusForEvent(ctx, eventID, domain.OUTCOME_STATUS_VOID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update all outcomes for event")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the unique bet_ids and how many outcomes have this bet_id
|
||||||
|
betIDSet := make(map[int64]int64)
|
||||||
|
for _, outcome := range outcomes {
|
||||||
|
betIDSet[outcome.BetID] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for betID := range betIDSet {
|
||||||
|
status, err := s.betSvc.CheckBetOutcomeForBet(ctx, betID)
|
||||||
|
if err != nil {
|
||||||
|
if err != bet.ErrOutcomesNotCompleted {
|
||||||
|
s.logger.Error("Failed to check bet outcome for bet", "event_id", eventID, "error", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.betSvc.UpdateStatus(ctx, betID, status)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update bet status", "event id", eventID, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
// for _, outcome := range outcomes {
|
||||||
|
// outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, domain.OUTCOME_STATUS_VOID)
|
||||||
|
// if err != nil {
|
||||||
|
// s.logger.Error("Failed to refund all outcome status", "bet_outcome_id", outcome.ID, "error", err)
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Check if all the bet outcomes have been set to refund for
|
||||||
|
// status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID)
|
||||||
|
// if err != nil {
|
||||||
|
// if err != bet.ErrOutcomesNotCompleted {
|
||||||
|
// s.logger.Error("Failed to check bet outcome for bet", "event_id", outcome.EventID, "error", err)
|
||||||
|
// }
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// err = s.betSvc.UpdateStatus(ctx, outcome.BetID, domain.OUTCOME_STATUS_VOID)
|
||||||
|
|
||||||
|
// if err != nil {
|
||||||
|
// s.logger.Error("Failed to update bet status", "event id", outcome.EventID, "error", err)
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
// TODO: Optimize this because there could be many bet outcomes for the same odd
|
// TODO: Optimize this because there could be many bet outcomes for the same odd
|
||||||
// Take market id and match result as param and update all the bet outcomes at the same time
|
// Take market id and match result as param and update all the bet outcomes at the same time
|
||||||
|
|
@ -58,29 +181,15 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
fmt.Printf("⚠️ Expired Events: %d \n", len(events))
|
fmt.Printf("⚠️ Expired Events: %d \n", len(events))
|
||||||
removed := 0
|
removed := 0
|
||||||
errs := make([]error, 0, len(events))
|
|
||||||
for i, event := range events {
|
for _, event := range events {
|
||||||
|
|
||||||
eventID, err := strconv.ParseInt(event.ID, 10, 64)
|
eventID, err := strconv.ParseInt(event.ID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to parse event id")
|
s.logger.Error("Failed to parse", "eventID", event.ID, "err", err)
|
||||||
errs = append(errs, fmt.Errorf("failed to parse event id %s: %w", event.ID, err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get pending bet outcomes", "error", err)
|
|
||||||
errs = append(errs, fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err))
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(outcomes) == 0 {
|
|
||||||
fmt.Printf("🕛 No bets have been placed on event %v (%d/%d) \n", event.ID, i+1, len(events))
|
|
||||||
} else {
|
|
||||||
fmt.Printf("✅ %d bets have been placed on event %v (%d/%d) \n", len(outcomes), event.ID, i+1, len(events))
|
|
||||||
}
|
|
||||||
|
|
||||||
isDeleted := true
|
|
||||||
result, err := s.fetchResult(ctx, eventID)
|
result, err := s.fetchResult(ctx, eventID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == ErrEventIsNotActive {
|
if err == ErrEventIsNotActive {
|
||||||
|
|
@ -108,78 +217,41 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Figure out what to do with the events that have been cancelled or postponed, etc...
|
// TODO: Figure out what to do with the events that have been cancelled or postponed, etc...
|
||||||
if timeStatusParsed != int64(domain.TIME_STATUS_ENDED) {
|
// if timeStatusParsed != int64(domain.TIME_STATUS_ENDED) {
|
||||||
s.logger.Warn("Event is not ended yet", "event_id", eventID, "time_status", commonResp.TimeStatus)
|
// s.logger.Warn("Event is not ended yet", "event_id", eventID, "time_status", commonResp.TimeStatus)
|
||||||
fmt.Printf("⚠️ Event %v is not ended yet (%d/%d) \n", event.ID, i+1, len(events))
|
// fmt.Printf("⚠️ Event %v is not ended yet (%d/%d) \n", event.ID, i+1, len(events))
|
||||||
isDeleted = false
|
// isDeleted = false
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
|
// notification := &domain.Notification{
|
||||||
|
// RecipientID: recipientID,
|
||||||
|
// Type: domain.NOTIFICATION_TYPE_WALLET,
|
||||||
|
// Level: domain.NotificationLevelWarning,
|
||||||
|
// Reciever: domain.NotificationRecieverSideAdmin,
|
||||||
|
// DeliveryChannel: domain.DeliveryChannelInApp,
|
||||||
|
// Payload: domain.NotificationPayload{
|
||||||
|
// Headline: "Wallet Threshold Alert",
|
||||||
|
// Message: message,
|
||||||
|
// },
|
||||||
|
// Priority: 2, // Medium priority
|
||||||
|
// }
|
||||||
|
|
||||||
|
switch timeStatusParsed {
|
||||||
|
case int64(domain.TIME_STATUS_NOT_STARTED), int64(domain.TIME_STATUS_IN_PLAY):
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
for j, outcome := range outcomes {
|
case int64(domain.TIME_STATUS_TO_BE_FIXED):
|
||||||
fmt.Printf("⚙️ Processing 🎲 outcomes '%v' for event %v(%v) (%d/%d) \n",
|
s.logger.Warn("Event needs to be rescheduled or corrected", "event_id", eventID)
|
||||||
outcome.MarketName,
|
// Admin users will be able to review the events
|
||||||
event.HomeTeam+" "+event.AwayTeam, event.ID,
|
|
||||||
j+1, len(outcomes))
|
|
||||||
|
|
||||||
if outcome.Expires.After(time.Now()) {
|
case int64(domain.TIME_STATUS_ENDED), int64(domain.TIME_STATUS_WALKOVER), int64(domain.TIME_STATUS_DECIDED_BY_FA):
|
||||||
isDeleted = false
|
err = s.UpdateResultForOutcomes(ctx, eventID, result.Results[0], sportID)
|
||||||
s.logger.Warn("Outcome is not expired yet", "event_id", event.ID, "outcome_id", outcome.ID)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
parseResult, err := s.parseResult(ctx, result.Results[0], outcome, sportID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
isDeleted = false
|
s.logger.Error("Error while updating result for event", "event_id", eventID)
|
||||||
s.logger.Error("Failed to parse result", "event_id", outcome.EventID, "outcome_id", outcome.ID, "error", err)
|
|
||||||
errs = append(errs, fmt.Errorf("failed to parse result for event %d: %w", outcome.EventID, err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, parseResult.Status)
|
|
||||||
if err != nil {
|
|
||||||
isDeleted = false
|
|
||||||
s.logger.Error("Failed to update bet outcome status", "bet_outcome_id", outcome.ID, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if outcome.Status == domain.OUTCOME_STATUS_ERROR || outcome.Status == domain.OUTCOME_STATUS_PENDING {
|
|
||||||
fmt.Printf("❌ Error while updating 🎲 outcomes '%v' for event %v(%v) (%d/%d) \n",
|
|
||||||
outcome.MarketName,
|
|
||||||
event.HomeTeam+" "+event.AwayTeam, event.ID,
|
|
||||||
j+1, len(outcomes))
|
|
||||||
|
|
||||||
s.logger.Error("Outcome is pending or error", "event_id", outcome.EventID, "outcome_id", outcome.ID)
|
|
||||||
isDeleted = false
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("✅ Successfully updated 🎲 outcomes '%v' for event %v(%v) (%d/%d) \n",
|
s.logger.Info("Removing Event", "eventID", event.ID)
|
||||||
outcome.MarketName,
|
|
||||||
event.HomeTeam+" "+event.AwayTeam, event.ID,
|
|
||||||
j+1, len(outcomes))
|
|
||||||
|
|
||||||
status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID)
|
|
||||||
if err != nil {
|
|
||||||
if err != bet.ErrOutcomesNotCompleted {
|
|
||||||
s.logger.Error("Failed to check bet outcome for bet", "event_id", outcome.EventID, "error", err)
|
|
||||||
}
|
|
||||||
isDeleted = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Printf("🧾 Updating bet %v - event %v (%d/%d) to %v\n", outcome.BetID, event.ID, j+1, len(outcomes), status.String())
|
|
||||||
err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to update bet status", "event id", outcome.EventID, "error", err)
|
|
||||||
isDeleted = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Printf("✅ Successfully updated 🎫 Bet %v - event %v(%v) (%d/%d) \n",
|
|
||||||
outcome.BetID,
|
|
||||||
event.HomeTeam+" "+event.AwayTeam, event.ID,
|
|
||||||
j+1, len(outcomes))
|
|
||||||
|
|
||||||
}
|
|
||||||
if isDeleted {
|
|
||||||
removed += 1
|
|
||||||
fmt.Printf("⚠️ Removing Event %v \n", event.ID)
|
|
||||||
err = s.repo.DeleteEvent(ctx, event.ID)
|
err = s.repo.DeleteEvent(ctx, event.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to remove event", "event_id", event.ID, "error", err)
|
s.logger.Error("Failed to remove event", "event_id", event.ID, "error", err)
|
||||||
|
|
@ -190,17 +262,91 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
s.logger.Error("Failed to remove odds for event", "event_id", event.ID, "error", err)
|
s.logger.Error("Failed to remove odds for event", "event_id", event.ID, "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
removed += 1
|
||||||
|
case int64(domain.TIME_STATUS_ABANDONED), int64(domain.TIME_STATUS_CANCELLED), int64(domain.TIME_STATUS_REMOVED):
|
||||||
|
s.logger.Info("Event abandoned/cancelled/removed", "event_id", eventID, "status", timeStatusParsed)
|
||||||
|
err = s.RefundAllOutcomes(ctx, eventID)
|
||||||
|
|
||||||
|
s.logger.Info("Removing Event", "eventID", event.ID)
|
||||||
|
err = s.repo.DeleteEvent(ctx, event.ID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to remove event", "event_id", event.ID, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.repo.DeleteOddsForEvent(ctx, event.ID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to remove odds for event", "event_id", event.ID, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
removed += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for j, outcome := range outcomes {
|
||||||
|
|
||||||
|
// fmt.Printf("⚙️ Processing 🎲 outcomes '%v' for event %v(%v) (%d/%d) \n",
|
||||||
|
// outcome.MarketName,
|
||||||
|
// event.HomeTeam+" "+event.AwayTeam, event.ID,
|
||||||
|
// j+1, len(outcomes))
|
||||||
|
|
||||||
|
// if outcome.Expires.After(time.Now()) {
|
||||||
|
// isDeleted = false
|
||||||
|
// s.logger.Warn("Outcome is not expired yet", "event_id", event.ID, "outcome_id", outcome.ID)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
|
// parseResult, err := s.parseResult(ctx, result.Results[0], outcome, sportID)
|
||||||
|
// if err != nil {
|
||||||
|
// isDeleted = false
|
||||||
|
// s.logger.Error("Failed to parse result", "event_id", outcome.EventID, "outcome_id", outcome.ID, "error", err)
|
||||||
|
// errs = append(errs, fmt.Errorf("failed to parse result for event %d: %w", outcome.EventID, err))
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
|
// outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, parseResult.Status)
|
||||||
|
// if err != nil {
|
||||||
|
// isDeleted = false
|
||||||
|
// s.logger.Error("Failed to update bet outcome status", "bet_outcome_id", outcome.ID, "error", err)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if outcome.Status == domain.OUTCOME_STATUS_ERROR || outcome.Status == domain.OUTCOME_STATUS_PENDING {
|
||||||
|
// fmt.Printf("❌ Error while updating 🎲 outcomes '%v' for event %v(%v) (%d/%d) \n",
|
||||||
|
// outcome.MarketName,
|
||||||
|
// event.HomeTeam+" "+event.AwayTeam, event.ID,
|
||||||
|
// j+1, len(outcomes))
|
||||||
|
|
||||||
|
// s.logger.Error("Outcome is pending or error", "event_id", outcome.EventID, "outcome_id", outcome.ID)
|
||||||
|
// isDeleted = false
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fmt.Printf("✅ Successfully updated 🎲 outcomes '%v' for event %v(%v) (%d/%d) \n",
|
||||||
|
// outcome.MarketName,
|
||||||
|
// event.HomeTeam+" "+event.AwayTeam, event.ID,
|
||||||
|
// j+1, len(outcomes))
|
||||||
|
|
||||||
|
// status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID)
|
||||||
|
// if err != nil {
|
||||||
|
// if err != bet.ErrOutcomesNotCompleted {
|
||||||
|
// s.logger.Error("Failed to check bet outcome for bet", "event_id", outcome.EventID, "error", err)
|
||||||
|
// }
|
||||||
|
// isDeleted = false
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fmt.Printf("🧾 Updating bet %v - event %v (%d/%d) to %v\n", outcome.BetID, event.ID, j+1, len(outcomes), status.String())
|
||||||
|
// err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status)
|
||||||
|
// if err != nil {
|
||||||
|
// s.logger.Error("Failed to update bet status", "event id", outcome.EventID, "error", err)
|
||||||
|
// isDeleted = false
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// fmt.Printf("✅ Successfully updated 🎫 Bet %v - event %v(%v) (%d/%d) \n",
|
||||||
|
// outcome.BetID,
|
||||||
|
// event.HomeTeam+" "+event.AwayTeam, event.ID,
|
||||||
|
// j+1, len(outcomes))
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
fmt.Printf("🗑️ Removed Events: %d \n", removed)
|
s.logger.Info("Total Number of Removed Events", "count", removed)
|
||||||
if len(errs) > 0 {
|
|
||||||
s.logger.Error("Errors occurred while processing results", "errors", errs)
|
|
||||||
for _, err := range errs {
|
|
||||||
fmt.Println("Error:", err)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("errors occurred while processing results: %v", errs)
|
|
||||||
}
|
|
||||||
s.logger.Info("Successfully processed results", "removed_events", removed, "total_events", len(events))
|
s.logger.Info("Successfully processed results", "removed_events", removed, "total_events", len(events))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -211,10 +357,9 @@ func (s *Service) CheckAndUpdateExpiredEvents(ctx context.Context) (int64, error
|
||||||
s.logger.Error("Failed to fetch events")
|
s.logger.Error("Failed to fetch events")
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
fmt.Printf("⚠️ Expired Events: %d \n", len(events))
|
|
||||||
updated := 0
|
updated := 0
|
||||||
for i, event := range events {
|
for _, event := range events {
|
||||||
fmt.Printf("⚙️ Processing event %v (%d/%d) \n", event.ID, i+1, len(events))
|
// fmt.Printf("⚙️ Processing event %v (%d/%d) \n", event.ID, i+1, len(events))
|
||||||
eventID, err := strconv.ParseInt(event.ID, 10, 64)
|
eventID, err := strconv.ParseInt(event.ID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to parse event id")
|
s.logger.Error("Failed to parse event id")
|
||||||
|
|
@ -232,7 +377,6 @@ func (s *Service) CheckAndUpdateExpiredEvents(ctx context.Context) (int64, error
|
||||||
}
|
}
|
||||||
if result.Success != 1 || len(result.Results) == 0 {
|
if result.Success != 1 || len(result.Results) == 0 {
|
||||||
s.logger.Error("Invalid API response", "event_id", eventID)
|
s.logger.Error("Invalid API response", "event_id", eventID)
|
||||||
fmt.Printf("⚠️ Invalid API response for event %v \n", result)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,12 +426,13 @@ func (s *Service) CheckAndUpdateExpiredEvents(ctx context.Context) (int64, error
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
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))
|
||||||
|
s.logger.Info("Updated Event Status", "event ID", event.ID, "status", eventStatus)
|
||||||
// Update the league because the league country code is only found on the result response
|
// Update the league because the league country code is only found on the result response
|
||||||
leagueID, err := strconv.ParseInt(commonResp.League.ID, 10, 64)
|
leagueID, err := strconv.ParseInt(commonResp.League.ID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ Invalid league id, leagueID %v", commonResp.League.ID)
|
// log.Printf("❌ Invalid league id, leagueID %v", commonResp.League.ID)
|
||||||
|
s.logger.Error("Invalid League ID", "leagueID", commonResp.League.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,12 +449,11 @@ func (s *Service) CheckAndUpdateExpiredEvents(ctx context.Context) (int64, error
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ Error Updating League %v", commonResp.League.Name)
|
s.logger.Error("Error Updating League", "League Name", commonResp.League.Name, "err", err)
|
||||||
log.Printf("err:%v", err)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("✅ Updated League %v with country code %v \n", leagueID, commonResp.League.CC)
|
// fmt.Printf("✅ Updated League %v with country code %v \n", leagueID, commonResp.League.CC)
|
||||||
|
s.logger.Info("Updated League with country code", "leagueID", leagueID, "code", commonResp.League.CC)
|
||||||
}
|
}
|
||||||
|
|
||||||
if updated == 0 {
|
if updated == 0 {
|
||||||
|
|
|
||||||
|
|
@ -46,50 +46,50 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
|
||||||
spec string
|
spec string
|
||||||
task func()
|
task func()
|
||||||
}{
|
}{
|
||||||
{
|
// {
|
||||||
spec: "0 0 * * * *", // Every 1 hour
|
// spec: "0 0 * * * *", // Every 1 hour
|
||||||
task: func() {
|
// task: func() {
|
||||||
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
|
// if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
|
||||||
log.Printf("FetchUpcomingEvents error: %v", err)
|
// log.Printf("FetchUpcomingEvents error: %v", err)
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
spec: "0 0 * * * *", // Every 15 minutes
|
// spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
|
||||||
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)
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
spec: "0 */5 * * * *", // Every 5 Minutes
|
spec: "0 */5 * * * *", // Every 5 Minutes
|
||||||
task: func() {
|
task: func() {
|
||||||
log.Println("Updating expired events status...")
|
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)
|
||||||
// } else {
|
} else {
|
||||||
// log.Printf("Successfully updated expired events")
|
log.Printf("Successfully updated expired events")
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// spec: "0 */15 * * * *", // Every 15 Minutes
|
spec: "0 */15 * * * *", // Every 15 Minutes
|
||||||
// task: func() {
|
task: func() {
|
||||||
// log.Println("Fetching results for upcoming events...")
|
log.Println("Fetching results for upcoming events...")
|
||||||
|
|
||||||
// if err := resultService.FetchAndProcessResults(context.Background()); err != nil {
|
if err := resultService.FetchAndProcessResults(context.Background()); err != nil {
|
||||||
// log.Printf("Failed to process result: %v", err)
|
log.Printf("Failed to process result: %v", err)
|
||||||
// } else {
|
} else {
|
||||||
// log.Printf("Successfully processed all outcomes")
|
log.Printf("Successfully processed all outcomes")
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, job := range schedule {
|
for _, job := range schedule {
|
||||||
// job.task()
|
job.task()
|
||||||
if _, err := c.AddFunc(job.spec, job.task); err != nil {
|
if _, err := c.AddFunc(job.spec, job.task); err != nil {
|
||||||
log.Fatalf("Failed to schedule cron job: %v", err)
|
log.Fatalf("Failed to schedule cron job: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,3 +106,32 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
|
||||||
|
|
||||||
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
|
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) SetLeagueAsFeatured(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)
|
||||||
|
}
|
||||||
|
leagueId, err := strconv.Atoi(leagueIdStr)
|
||||||
|
if err != nil {
|
||||||
|
response.WriteJSON(c, fiber.StatusBadRequest, "invalid league id", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var req SetLeagueActiveReq
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
h.logger.Error("SetLeagueReq failed", "error", err)
|
||||||
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Failed to parse request", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
valErrs, ok := h.validator.Validate(c, req)
|
||||||
|
if !ok {
|
||||||
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.leagueSvc.SetLeagueActive(c.Context(), int64(leagueId), req.IsActive); err != nil {
|
||||||
|
response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update league", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,8 +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 {
|
||||||
fmt.Printf("Failed to fetch raw odds: %v market_id:%v upcomingID:%v\n", err, marketID, upcomingID)
|
// 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)
|
h.logger.Error("Failed to get raw odds by market ID", "marketID", marketID, "upcomingID", upcomingID, "error", err)
|
||||||
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve raw odds", err, nil)
|
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve raw odds", err, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,6 +172,62 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TopLeaguesRes struct {
|
||||||
|
Leagues []TopLeague `json:"leagues"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TopLeague struct {
|
||||||
|
LeagueID int64 `json:"league_id"`
|
||||||
|
LeagueName string `json:"league_name"`
|
||||||
|
Events []domain.UpcomingEvent `json:"events"`
|
||||||
|
// Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Retrieve all top leagues
|
||||||
|
// @Description Retrieve all top leagues
|
||||||
|
// @Tags prematch
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} domain.UpcomingEvent
|
||||||
|
// @Failure 500 {object} response.APIResponse
|
||||||
|
// @Router /top-leagues [get]
|
||||||
|
func (h *Handler) GetTopLeagues(c *fiber.Ctx) error {
|
||||||
|
|
||||||
|
leagues, err := h.leagueSvc.GetFeaturedLeagues(c.Context())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("Error while fetching top leagues", "err", err)
|
||||||
|
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get featured leagues", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var topLeague []TopLeague = make([]TopLeague, 0, len(leagues))
|
||||||
|
for _, league := range leagues {
|
||||||
|
events, _, err := h.eventSvc.GetPaginatedUpcomingEvents(
|
||||||
|
c.Context(), domain.EventFilter{
|
||||||
|
LeagueID: domain.ValidInt32{
|
||||||
|
Value: int32(league.ID),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error while fetching events for top league %v \n", league.ID)
|
||||||
|
h.logger.Error("Error while fetching events for top league", "League ID", league.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
topLeague = append(topLeague, TopLeague{
|
||||||
|
LeagueID: league.ID,
|
||||||
|
LeagueName: league.Name,
|
||||||
|
Events: events,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
res := TopLeaguesRes{
|
||||||
|
Leagues: topLeague,
|
||||||
|
}
|
||||||
|
return response.WriteJSON(c, fiber.StatusOK, "All top leagues events retrieved successfully", res, nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary Retrieve an upcoming by ID
|
// @Summary Retrieve an upcoming by ID
|
||||||
// @Description Retrieve an upcoming event by ID
|
// @Description Retrieve an upcoming event by ID
|
||||||
// @Tags prematch
|
// @Tags prematch
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ func (a *App) initAppRoutes() {
|
||||||
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)
|
||||||
a.fiber.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved)
|
a.fiber.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved)
|
||||||
|
a.fiber.Get("/top-leagues", h.GetTopLeagues)
|
||||||
|
|
||||||
// Leagues
|
// Leagues
|
||||||
a.fiber.Get("/leagues", h.GetAllLeagues)
|
a.fiber.Get("/leagues", h.GetAllLeagues)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user