league support check from db

This commit is contained in:
Asher Samuel 2025-06-06 03:36:15 +03:00
parent eb4f267142
commit aedefbdb0b
9 changed files with 283 additions and 67 deletions

View File

@ -75,4 +75,5 @@ DROP TABLE IF EXISTS supported_operations;
DROP TABLE IF EXISTS refresh_tokens;
DROP TABLE IF EXISTS otps;
DROP TABLE IF EXISTS odds;
DROP TABLE IF EXISTS events;
DROP TABLE IF EXISTS events;
DROP TABLE IF EXISTS leagues;

View File

@ -232,6 +232,13 @@ CREATE TABLE companies (
admin_id BIGINT NOT NULL,
wallet_id BIGINT NOT NULL
);
CREATE TABLE leagues (
id BIGINT PRIMARY KEY,
name TEXT NOT NULL,
country_code TEXT,
bet365_id INT,
is_active BOOLEAN DEFAULT true
);
-- Views
CREATE VIEW companies_details AS
SELECT companies.*,

37
db/query/leagues.sql Normal file
View File

@ -0,0 +1,37 @@
-- name: InsertLeague :exec
INSERT INTO leagues (
id,
name,
country_code,
bet365_id,
is_active
) VALUES (
$1, $2, $3, $4, $5
)
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;
-- name: CheckLeagueSupport :one
SELECT EXISTS(
SELECT 1
FROM leagues
WHERE id = $1
AND is_active = true
);
-- name: UpdateLeague :exec
UPDATE leagues
SET name = $1,
country_code = $2,
bet365_id = $3,
is_active = $4
WHERE id = $5;

128
gen/db/leagues.sql.go Normal file
View File

@ -0,0 +1,128 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: leagues.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CheckLeagueSupport = `-- name: CheckLeagueSupport :one
SELECT EXISTS(
SELECT 1
FROM leagues
WHERE id = $1
AND is_active = true
)
`
func (q *Queries) CheckLeagueSupport(ctx context.Context, id int64) (bool, error) {
row := q.db.QueryRow(ctx, CheckLeagueSupport, id)
var exists bool
err := row.Scan(&exists)
return exists, err
}
const GetSupportedLeagues = `-- name: GetSupportedLeagues :many
SELECT id,
name,
country_code,
bet365_id,
is_active
FROM leagues
WHERE is_active = true
`
func (q *Queries) GetSupportedLeagues(ctx context.Context) ([]League, error) {
rows, err := q.db.Query(ctx, GetSupportedLeagues)
if err != nil {
return nil, err
}
defer rows.Close()
var items []League
for rows.Next() {
var i League
if err := rows.Scan(
&i.ID,
&i.Name,
&i.CountryCode,
&i.Bet365ID,
&i.IsActive,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const InsertLeague = `-- name: InsertLeague :exec
INSERT INTO leagues (
id,
name,
country_code,
bet365_id,
is_active
) VALUES (
$1, $2, $3, $4, $5
)
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id,
is_active = EXCLUDED.is_active
`
type InsertLeagueParams 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"`
}
func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) error {
_, err := q.db.Exec(ctx, InsertLeague,
arg.ID,
arg.Name,
arg.CountryCode,
arg.Bet365ID,
arg.IsActive,
)
return err
}
const UpdateLeague = `-- name: UpdateLeague :exec
UPDATE leagues
SET name = $1,
country_code = $2,
bet365_id = $3,
is_active = $4
WHERE id = $5
`
type UpdateLeagueParams struct {
Name string `json:"name"`
CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"`
IsActive pgtype.Bool `json:"is_active"`
ID int64 `json:"id"`
}
func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) error {
_, err := q.db.Exec(ctx, UpdateLeague,
arg.Name,
arg.CountryCode,
arg.Bet365ID,
arg.IsActive,
arg.ID,
)
return err
}

View File

@ -199,6 +199,14 @@ type Event struct {
Source pgtype.Text `json:"source"`
}
type League 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"`
}
type Notification struct {
ID string `json:"id"`
RecipientID int64 `json:"recipient_id"`

View File

@ -1,39 +1,9 @@
package domain
// TODO Will make this dynamic by moving into the database
var SupportedLeagues = []int64{
// Football
10041282, //Premier League
10083364, //La Liga
10041095, //German Bundesliga
10041100, //Ligue 1
10041809, //UEFA Champions League
10041957, //UEFA Europa League
10079560, //UEFA Conference League
10047168, // US MLS
10044469, // Ethiopian Premier League
10050282, //UEFA Nations League
10043156, //England FA Cup
10042103, //France Cup
10041088, //Premier League 2
10084250, //Turkiye Super League
10041187, //Kenya Super League
10041315, //Italian Serie A
10041391, //Netherlands Eredivisie
// Basketball
173998768, //NBA
10041830, //NBA
10049984, //WNBA
10037165, //German Bundesliga
10036608, //Italian Lega 1
10040795, //EuroLeague
// Ice Hockey
10037477, //NHL
10037447, //AHL
10069385, //IIHF World Championship
type League struct {
ID int64
Name string
CountryCode string
Bet365ID int32
IsActive bool
}

View File

@ -9,7 +9,7 @@ type BaseResultResponse struct {
Results []json.RawMessage `json:"results"`
}
type League struct {
type LeagueRes struct {
ID string `json:"id"`
Name string `json:"name"`
CC string `json:"cc"`
@ -28,14 +28,14 @@ type Score struct {
}
type FootballResultResponse 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"`
Scores struct {
FirstHalf Score `json:"1"`
SecondHalf Score `json:"2"`
@ -67,14 +67,14 @@ type FootballResultResponse struct {
}
type BasketballResultResponse 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"`
Scores struct {
FirstQuarter Score `json:"1"`
SecondQuarter Score `json:"2"`
@ -114,14 +114,14 @@ type BasketballResultResponse struct {
Bet365ID string `json:"bet365_id"`
}
type IceHockeyResultResponse 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"`
Scores struct {
FirstPeriod Score `json:"1"`
SecondPeriod Score `json:"2"`

View File

@ -0,0 +1,61 @@
package repository
import (
"context"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) SaveLeague(ctx context.Context, l domain.League) error {
return s.queries.InsertLeague(ctx, dbgen.InsertLeagueParams{
ID: l.ID,
Name: l.Name,
CountryCode: pgtype.Text{String: l.CountryCode, Valid: true},
Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true},
IsActive: pgtype.Bool{Bool: l.IsActive, Valid: true},
})
}
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) CheckLeagueSupport(ctx context.Context, leagueID int64) (bool, error) {
return s.queries.CheckLeagueSupport(ctx, leagueID)
}
// TODO: change to only take league id instad of the whole league
func (s *Store) SetLeagueActive(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: true, Valid: true},
})
}
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},
})
}

View File

@ -7,7 +7,6 @@ import (
"io"
"log"
"net/http"
"slices"
"strconv"
"sync"
"time"
@ -242,11 +241,16 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, sour
continue
}
if !slices.Contains(domain.SupportedLeagues, leagueID) {
// fmt.Printf("⚠️ Skipping league %s (%d) as it is not supported\n", ev.League.Name, leagueID)
// doesn't make sense to save and check back to back, but for now it can be here
s.store.SaveLeague(ctx, domain.League{
ID: leagueID,
Name: ev.League.Name,
IsActive: true,
})
if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil {
skippedLeague = append(skippedLeague, ev.League.Name)
// ! for now
// continue
continue
}
event := domain.UpcomingEvent{