fix odd filtering
This commit is contained in:
parent
252bf04b1e
commit
8e271559ae
|
|
@ -69,7 +69,7 @@ func main() {
|
||||||
userSvc := user.NewService(store, store, mockSms, mockEmail)
|
userSvc := user.NewService(store, store, mockSms, mockEmail)
|
||||||
|
|
||||||
eventSvc := event.New(cfg.Bet365Token, store)
|
eventSvc := event.New(cfg.Bet365Token, store)
|
||||||
oddsSvc := odds.New(cfg.Bet365Token, store)
|
oddsSvc := odds.New(store, cfg, logger)
|
||||||
resultSvc := result.NewService(store, cfg, logger)
|
resultSvc := result.NewService(store, cfg, logger)
|
||||||
ticketSvc := ticket.NewService(store)
|
ticketSvc := ticket.NewService(store)
|
||||||
betSvc := bet.NewService(store)
|
betSvc := bet.NewService(store)
|
||||||
|
|
|
||||||
50
internal/domain/oddres.go
Normal file
50
internal/domain/oddres.go
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type BaseNonLiveOddResponse struct {
|
||||||
|
Success int `json:"success"`
|
||||||
|
Results []json.RawMessage `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OddsSection struct {
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
Sp map[string]OddsMarket `json:"sp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OddsMarket struct {
|
||||||
|
ID json.Number `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Odds []json.RawMessage `json:"odds"`
|
||||||
|
Header string `json:"header,omitempty"`
|
||||||
|
Handicap string `json:"handicap,omitempty"`
|
||||||
|
Open int64 `json:"open,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FootballOddsResponse struct {
|
||||||
|
EventID string `json:"event_id"`
|
||||||
|
FI string `json:"FI"`
|
||||||
|
Main OddsSection `json:"main"`
|
||||||
|
AsianLines OddsSection `json:"asian_lines"`
|
||||||
|
Goals OddsSection `json:"goals"`
|
||||||
|
Half OddsSection `json:"half"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasketballOddsResponse struct {
|
||||||
|
EventID string `json:"event_id"`
|
||||||
|
FI string `json:"FI"`
|
||||||
|
Main OddsSection `json:"main"`
|
||||||
|
HalfProps OddsSection `json:"half_props"`
|
||||||
|
QuarterProps OddsSection `json:"quarter_props"`
|
||||||
|
TeamProps OddsSection `json:"team_props"`
|
||||||
|
Others []OddsSection `json:"others"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IceHockeyOddsResponse struct {
|
||||||
|
EventID string `json:"event_id"`
|
||||||
|
FI string `json:"FI"`
|
||||||
|
Main OddsSection `json:"main"`
|
||||||
|
Main2 OddsSection `json:"main_2"`
|
||||||
|
FirstPeriod OddsSection `json:"1st_period"`
|
||||||
|
Others []OddsSection `json:"others"`
|
||||||
|
}
|
||||||
|
|
@ -1,185 +1,9 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseResultResponse struct {
|
|
||||||
Success int `json:"success"`
|
|
||||||
Results []json.RawMessage `json:"results"`
|
|
||||||
}
|
|
||||||
type FootballResultResponse struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
SportID string `json:"sport_id"`
|
|
||||||
Time string `json:"time"`
|
|
||||||
TimeStatus string `json:"time_status"`
|
|
||||||
League struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"league"`
|
|
||||||
Home struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
ImageID string `json:"image_id"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"home"`
|
|
||||||
Away struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
ImageID string `json:"image_id"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"away"`
|
|
||||||
SS string `json:"ss"`
|
|
||||||
Scores struct {
|
|
||||||
FirstHalf Score `json:"1"`
|
|
||||||
SecondHalf Score `json:"2"`
|
|
||||||
} `json:"scores"`
|
|
||||||
Stats struct {
|
|
||||||
Attacks []string `json:"attacks"`
|
|
||||||
Corners []string `json:"corners"`
|
|
||||||
DangerousAttacks []string `json:"dangerous_attacks"`
|
|
||||||
Goals []string `json:"goals"`
|
|
||||||
OffTarget []string `json:"off_target"`
|
|
||||||
OnTarget []string `json:"on_target"`
|
|
||||||
Penalties []string `json:"penalties"`
|
|
||||||
PossessionRT []string `json:"possession_rt"`
|
|
||||||
RedCards []string `json:"redcards"`
|
|
||||||
Substitutions []string `json:"substitutions"`
|
|
||||||
YellowCards []string `json:"yellowcards"`
|
|
||||||
} `json:"stats"`
|
|
||||||
Extra struct {
|
|
||||||
HomePos string `json:"home_pos"`
|
|
||||||
AwayPos string `json:"away_pos"`
|
|
||||||
StadiumData map[string]string `json:"stadium_data"`
|
|
||||||
Round string `json:"round"`
|
|
||||||
} `json:"extra"`
|
|
||||||
Events []map[string]string `json:"events"`
|
|
||||||
HasLineup int `json:"has_lineup"`
|
|
||||||
ConfirmedAt string `json:"confirmed_at"`
|
|
||||||
Bet365ID string `json:"bet365_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BasketballResultResponse struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
SportID string `json:"sport_id"`
|
|
||||||
Time string `json:"time"`
|
|
||||||
TimeStatus string `json:"time_status"`
|
|
||||||
League struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"league"`
|
|
||||||
Home struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
ImageID string `json:"image_id"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"home"`
|
|
||||||
Away struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
ImageID string `json:"image_id"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"away"`
|
|
||||||
SS string `json:"ss"`
|
|
||||||
Scores struct {
|
|
||||||
FirstQuarter Score `json:"1"`
|
|
||||||
SecondQuarter Score `json:"2"`
|
|
||||||
FirstHalf Score `json:"3"`
|
|
||||||
ThirdQuarter Score `json:"4"`
|
|
||||||
FourthQuarter Score `json:"5"`
|
|
||||||
TotalScore Score `json:"7"`
|
|
||||||
} `json:"scores"`
|
|
||||||
Stats struct {
|
|
||||||
TwoPoints []string `json:"2points"`
|
|
||||||
ThreePoints []string `json:"3points"`
|
|
||||||
BiggestLead []string `json:"biggest_lead"`
|
|
||||||
Fouls []string `json:"fouls"`
|
|
||||||
FreeThrows []string `json:"free_throws"`
|
|
||||||
FreeThrowRate []string `json:"free_throws_rate"`
|
|
||||||
LeadChanges []string `json:"lead_changes"`
|
|
||||||
MaxpointsInarow []string `json:"maxpoints_inarow"`
|
|
||||||
Possession []string `json:"possession"`
|
|
||||||
SuccessAttempts []string `json:"success_attempts"`
|
|
||||||
TimeSpendInLead []string `json:"timespent_inlead"`
|
|
||||||
Timeuts []string `json:"time_outs"`
|
|
||||||
} `json:"stats"`
|
|
||||||
Extra struct {
|
|
||||||
HomePos string `json:"home_pos"`
|
|
||||||
AwayPos string `json:"away_pos"`
|
|
||||||
AwayManager map[string]string `json:"away_manager"`
|
|
||||||
HomeManager map[string]string `json:"home_manager"`
|
|
||||||
NumberOfPeriods string `json:"numberofperiods"`
|
|
||||||
PeriodLength string `json:"periodlength"`
|
|
||||||
StadiumData map[string]string `json:"stadium_data"`
|
|
||||||
Length string `json:"length"`
|
|
||||||
Round string `json:"round"`
|
|
||||||
} `json:"extra"`
|
|
||||||
Events []map[string]string `json:"events"`
|
|
||||||
HasLineup int `json:"has_lineup"`
|
|
||||||
ConfirmedAt string `json:"confirmed_at"`
|
|
||||||
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 struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"league"`
|
|
||||||
Home struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
ImageID string `json:"image_id"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"home"`
|
|
||||||
Away struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
ImageID string `json:"image_id"`
|
|
||||||
CC string `json:"cc"`
|
|
||||||
} `json:"away"`
|
|
||||||
SS string `json:"ss"`
|
|
||||||
Scores struct {
|
|
||||||
FirstPeriod Score `json:"1"`
|
|
||||||
SecondPeriod Score `json:"2"`
|
|
||||||
ThirdPeriod Score `json:"3"`
|
|
||||||
TotalScore Score `json:"5"`
|
|
||||||
} `json:"scores"`
|
|
||||||
|
|
||||||
Stats struct {
|
|
||||||
Shots []string `json:"shots"`
|
|
||||||
Penalties []string `json:"penalties"`
|
|
||||||
GoalsOnPowerPlay []string `json:"goals_on_power_play"`
|
|
||||||
SSeven []string `json:"s7"`
|
|
||||||
} `json:"stats"`
|
|
||||||
Extra struct {
|
|
||||||
HomePos string `json:"home_pos"`
|
|
||||||
AwayPos string `json:"away_pos"`
|
|
||||||
AwayManager map[string]string `json:"away_manager"`
|
|
||||||
HomeManager map[string]string `json:"home_manager"`
|
|
||||||
NumberOfPeriods string `json:"numberofperiods"`
|
|
||||||
PeriodLength string `json:"periodlength"`
|
|
||||||
StadiumData map[string]string `json:"stadium_data"`
|
|
||||||
Length string `json:"length"`
|
|
||||||
Round string `json:"round"`
|
|
||||||
} `json:"extra"`
|
|
||||||
Events []map[string]string `json:"events"`
|
|
||||||
HasLineup int `json:"has_lineup"`
|
|
||||||
ConfirmedAt string `json:"confirmed_at"`
|
|
||||||
Bet365ID string `json:"bet365_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Score struct {
|
|
||||||
Home string `json:"home"`
|
|
||||||
Away string `json:"away"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MarketConfig struct {
|
type MarketConfig struct {
|
||||||
Sport string
|
Sport string
|
||||||
MarketCategories map[string]bool
|
MarketCategories map[string]bool
|
||||||
|
|
|
||||||
152
internal/domain/resultres.go
Normal file
152
internal/domain/resultres.go
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseResultResponse struct {
|
||||||
|
Success int `json:"success"`
|
||||||
|
Results []json.RawMessage `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type League struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CC string `json:"cc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Team struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ImageID string `json:"image_id"`
|
||||||
|
CC string `json:"cc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Score struct {
|
||||||
|
Home string `json:"home"`
|
||||||
|
Away string `json:"away"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
Scores struct {
|
||||||
|
FirstHalf Score `json:"1"`
|
||||||
|
SecondHalf Score `json:"2"`
|
||||||
|
} `json:"scores"`
|
||||||
|
Stats struct {
|
||||||
|
Attacks []string `json:"attacks"`
|
||||||
|
Corners []string `json:"corners"`
|
||||||
|
DangerousAttacks []string `json:"dangerous_attacks"`
|
||||||
|
Goals []string `json:"goals"`
|
||||||
|
OffTarget []string `json:"off_target"`
|
||||||
|
OnTarget []string `json:"on_target"`
|
||||||
|
Penalties []string `json:"penalties"`
|
||||||
|
PossessionRT []string `json:"possession_rt"`
|
||||||
|
RedCards []string `json:"redcards"`
|
||||||
|
Substitutions []string `json:"substitutions"`
|
||||||
|
YellowCards []string `json:"yellowcards"`
|
||||||
|
} `json:"stats"`
|
||||||
|
Extra struct {
|
||||||
|
HomePos string `json:"home_pos"`
|
||||||
|
AwayPos string `json:"away_pos"`
|
||||||
|
StadiumData map[string]string `json:"stadium_data"`
|
||||||
|
Round string `json:"round"`
|
||||||
|
} `json:"extra"`
|
||||||
|
Events []map[string]string `json:"events"`
|
||||||
|
HasLineup int `json:"has_lineup"`
|
||||||
|
ConfirmedAt string `json:"confirmed_at"`
|
||||||
|
Bet365ID string `json:"bet365_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
Scores struct {
|
||||||
|
FirstQuarter Score `json:"1"`
|
||||||
|
SecondQuarter Score `json:"2"`
|
||||||
|
FirstHalf Score `json:"3"`
|
||||||
|
ThirdQuarter Score `json:"4"`
|
||||||
|
FourthQuarter Score `json:"5"`
|
||||||
|
TotalScore Score `json:"7"`
|
||||||
|
} `json:"scores"`
|
||||||
|
Stats struct {
|
||||||
|
TwoPoints []string `json:"2points"`
|
||||||
|
ThreePoints []string `json:"3points"`
|
||||||
|
BiggestLead []string `json:"biggest_lead"`
|
||||||
|
Fouls []string `json:"fouls"`
|
||||||
|
FreeThrows []string `json:"free_throws"`
|
||||||
|
FreeThrowRate []string `json:"free_throws_rate"`
|
||||||
|
LeadChanges []string `json:"lead_changes"`
|
||||||
|
MaxpointsInarow []string `json:"maxpoints_inarow"`
|
||||||
|
Possession []string `json:"possession"`
|
||||||
|
SuccessAttempts []string `json:"success_attempts"`
|
||||||
|
TimeSpendInLead []string `json:"timespent_inlead"`
|
||||||
|
Timeuts []string `json:"time_outs"`
|
||||||
|
} `json:"stats"`
|
||||||
|
Extra struct {
|
||||||
|
HomePos string `json:"home_pos"`
|
||||||
|
AwayPos string `json:"away_pos"`
|
||||||
|
AwayManager map[string]string `json:"away_manager"`
|
||||||
|
HomeManager map[string]string `json:"home_manager"`
|
||||||
|
NumberOfPeriods string `json:"numberofperiods"`
|
||||||
|
PeriodLength string `json:"periodlength"`
|
||||||
|
StadiumData map[string]string `json:"stadium_data"`
|
||||||
|
Length string `json:"length"`
|
||||||
|
Round string `json:"round"`
|
||||||
|
} `json:"extra"`
|
||||||
|
Events []map[string]string `json:"events"`
|
||||||
|
HasLineup int `json:"has_lineup"`
|
||||||
|
ConfirmedAt string `json:"confirmed_at"`
|
||||||
|
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"`
|
||||||
|
Scores struct {
|
||||||
|
FirstPeriod Score `json:"1"`
|
||||||
|
SecondPeriod Score `json:"2"`
|
||||||
|
ThirdPeriod Score `json:"3"`
|
||||||
|
TotalScore Score `json:"5"`
|
||||||
|
} `json:"scores"`
|
||||||
|
|
||||||
|
Stats struct {
|
||||||
|
Shots []string `json:"shots"`
|
||||||
|
Penalties []string `json:"penalties"`
|
||||||
|
GoalsOnPowerPlay []string `json:"goals_on_power_play"`
|
||||||
|
SSeven []string `json:"s7"`
|
||||||
|
} `json:"stats"`
|
||||||
|
Extra struct {
|
||||||
|
HomePos string `json:"home_pos"`
|
||||||
|
AwayPos string `json:"away_pos"`
|
||||||
|
AwayManager map[string]string `json:"away_manager"`
|
||||||
|
HomeManager map[string]string `json:"home_manager"`
|
||||||
|
NumberOfPeriods string `json:"numberofperiods"`
|
||||||
|
PeriodLength string `json:"periodlength"`
|
||||||
|
StadiumData map[string]string `json:"stadium_data"`
|
||||||
|
Length string `json:"length"`
|
||||||
|
Round string `json:"round"`
|
||||||
|
} `json:"extra"`
|
||||||
|
Events []map[string]string `json:"events"`
|
||||||
|
HasLineup int `json:"has_lineup"`
|
||||||
|
ConfirmedAt string `json:"confirmed_at"`
|
||||||
|
Bet365ID string `json:"bet365_id"`
|
||||||
|
}
|
||||||
|
|
@ -3,26 +3,37 @@ package odds
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceImpl struct {
|
type ServiceImpl struct {
|
||||||
token string
|
store *repository.Store
|
||||||
store *repository.Store
|
config *config.Config
|
||||||
|
logger *slog.Logger
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(token string, store *repository.Store) *ServiceImpl {
|
func New(store *repository.Store, cfg *config.Config, logger *slog.Logger) *ServiceImpl {
|
||||||
return &ServiceImpl{token: token, store: store}
|
return &ServiceImpl{
|
||||||
|
store: store,
|
||||||
|
config: cfg,
|
||||||
|
logger: logger,
|
||||||
|
client: &http.Client{Timeout: 10 * time.Second},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this is only getting the main odds, this must be fixed
|
// TODO Add the optimization to get 10 events at the same time
|
||||||
func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error {
|
func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error {
|
||||||
eventIDs, err := s.store.GetAllUpcomingEvents(ctx)
|
eventIDs, err := s.store.GetAllUpcomingEvents(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -30,60 +41,208 @@ func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
for _, event := range eventIDs {
|
for _, event := range eventIDs {
|
||||||
// time.Sleep(3 * time.Second) //This will restrict the fetching to 1200 requests per hour
|
// time.Sleep(3 * time.Second) //This will restrict the fetching to 1200 requests per hour
|
||||||
|
|
||||||
eventID := event.ID
|
eventID, err := strconv.ParseInt(event.ID, 10, 64)
|
||||||
prematchURL := "https://api.b365api.com/v3/bet365/prematch?token=" + s.token + "&FI=" + eventID
|
|
||||||
log.Printf("📡 Fetching prematch odds for event ID: %s", eventID)
|
|
||||||
resp, err := http.Get(prematchURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ Failed to fetch prematch odds for event %s: %v", eventID, err)
|
s.logger.Error("Failed to parse event id")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://api.b365api.com/v3/bet365/prematch?token=%s&FI=%d", s.config.Bet365Token, eventID)
|
||||||
|
|
||||||
|
log.Printf("📡 Fetching prematch odds for event ID: %d", eventID)
|
||||||
|
|
||||||
|
resp, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ Failed to fetch prematch odds for event %d: %v", eventID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
var oddsData struct {
|
var oddsData domain.BaseNonLiveOddResponse
|
||||||
Success int `json:"success"`
|
|
||||||
Results []struct {
|
|
||||||
EventID string `json:"event_id"`
|
|
||||||
FI string `json:"FI"`
|
|
||||||
Main OddsSection `json:"main"`
|
|
||||||
} `json:"results"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(body, &oddsData); err != nil || oddsData.Success != 1 || len(oddsData.Results) == 0 {
|
if err := json.Unmarshal(body, &oddsData); err != nil || oddsData.Success != 1 || len(oddsData.Results) == 0 {
|
||||||
log.Printf("❌ Invalid prematch data for event %s", eventID)
|
log.Printf("❌ Invalid prematch data for event %d", eventID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
result := oddsData.Results[0]
|
sportID, err := strconv.ParseInt(event.SportID, 10, 64)
|
||||||
finalID := result.EventID
|
|
||||||
if finalID == "" {
|
switch sportID {
|
||||||
finalID = result.FI
|
case domain.FOOTBALL:
|
||||||
|
if err := s.parseFootball(ctx, oddsData.Results[0]); err != nil {
|
||||||
|
s.logger.Error("Failed to insert football odd")
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
case domain.BASKETBALL:
|
||||||
|
if err := s.parseBasketball(ctx, oddsData.Results[0]); err != nil {
|
||||||
|
s.logger.Error("Failed to insert basketball odd")
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
case domain.ICE_HOCKEY:
|
||||||
|
if err := s.parseIceHockey(ctx, oddsData.Results[0]); err != nil {
|
||||||
|
s.logger.Error("Failed to insert ice hockey odd")
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if finalID == "" {
|
|
||||||
log.Printf("⚠️ Skipping event %s with no valid ID", eventID)
|
// result := oddsData.Results[0]
|
||||||
continue
|
|
||||||
}
|
|
||||||
s.storeSection(ctx, finalID, result.FI, "main", result.Main)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName string, section OddsSection) {
|
func (s *ServiceImpl) parseFootball(ctx context.Context, res json.RawMessage) error {
|
||||||
|
var footballRes domain.FootballOddsResponse
|
||||||
|
if err := json.Unmarshal(res, &footballRes); err != nil {
|
||||||
|
s.logger.Error("Failed to unmarshal football result", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if footballRes.EventID == "" && footballRes.FI == "" {
|
||||||
|
s.logger.Error("Skipping result with no valid Event ID")
|
||||||
|
return fmt.Errorf("Skipping result with no valid Event ID")
|
||||||
|
}
|
||||||
|
sections := map[string]domain.OddsSection{
|
||||||
|
"main": footballRes.Main,
|
||||||
|
"asian_lines": footballRes.AsianLines,
|
||||||
|
"goals": footballRes.Goals,
|
||||||
|
"half": footballRes.Half,
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
for oddCategory, section := range sections {
|
||||||
|
if err := s.storeSection(ctx, footballRes.EventID, footballRes.FI, oddCategory, section); err != nil {
|
||||||
|
s.logger.Error("Skipping result with no valid Event ID")
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceImpl) parseBasketball(ctx context.Context, res json.RawMessage) error {
|
||||||
|
var basketballRes domain.BasketballOddsResponse
|
||||||
|
if err := json.Unmarshal(res, &basketballRes); err != nil {
|
||||||
|
s.logger.Error("Failed to unmarshal football result", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if basketballRes.EventID == "" && basketballRes.FI == "" {
|
||||||
|
s.logger.Error("Skipping result with no valid Event ID")
|
||||||
|
return fmt.Errorf("Skipping result with no valid Event ID")
|
||||||
|
}
|
||||||
|
sections := map[string]domain.OddsSection{
|
||||||
|
"main": basketballRes.Main,
|
||||||
|
"half_props": basketballRes.HalfProps,
|
||||||
|
"quarter_props": basketballRes.QuarterProps,
|
||||||
|
"team_props": basketballRes.TeamProps,
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
for oddCategory, section := range sections {
|
||||||
|
if err := s.storeSection(ctx, basketballRes.EventID, basketballRes.FI, oddCategory, section); err != nil {
|
||||||
|
s.logger.Error("Skipping result with no valid Event ID")
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, section := range basketballRes.Others {
|
||||||
|
if err := s.storeSection(ctx, basketballRes.EventID, basketballRes.FI, "others", section); err != nil {
|
||||||
|
s.logger.Error("Skipping result with no valid Event ID")
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (s *ServiceImpl) parseIceHockey(ctx context.Context, res json.RawMessage) error {
|
||||||
|
var iceHockeyRes domain.IceHockeyOddsResponse
|
||||||
|
if err := json.Unmarshal(res, &iceHockeyRes); err != nil {
|
||||||
|
s.logger.Error("Failed to unmarshal football result", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if iceHockeyRes.EventID == "" && iceHockeyRes.FI == "" {
|
||||||
|
s.logger.Error("Skipping result with no valid Event ID")
|
||||||
|
return fmt.Errorf("Skipping result with no valid Event ID")
|
||||||
|
}
|
||||||
|
sections := map[string]domain.OddsSection{
|
||||||
|
"main": iceHockeyRes.Main,
|
||||||
|
"main_2": iceHockeyRes.Main2,
|
||||||
|
"1st_period": iceHockeyRes.FirstPeriod,
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
for oddCategory, section := range sections {
|
||||||
|
if err := s.storeSection(ctx, iceHockeyRes.EventID, iceHockeyRes.FI, oddCategory, section); err != nil {
|
||||||
|
s.logger.Error("Skipping result with no valid Event ID")
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, section := range iceHockeyRes.Others {
|
||||||
|
if err := s.storeSection(ctx, iceHockeyRes.EventID, iceHockeyRes.FI, "others", section); err != nil {
|
||||||
|
s.logger.Error("Skipping result with no valid Event ID")
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName string, section domain.OddsSection) error {
|
||||||
if len(section.Sp) == 0 {
|
if len(section.Sp) == 0 {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedAtUnix, _ := strconv.ParseInt(section.UpdatedAt, 10, 64)
|
updatedAtUnix, _ := strconv.ParseInt(section.UpdatedAt, 10, 64)
|
||||||
updatedAt := time.Unix(updatedAtUnix, 0)
|
updatedAt := time.Unix(updatedAtUnix, 0)
|
||||||
|
|
||||||
|
var errs []error
|
||||||
for marketType, market := range section.Sp {
|
for marketType, market := range section.Sp {
|
||||||
if len(market.Odds) == 0 {
|
if len(market.Odds) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
marketID, err := market.ID.Int64()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Invalid market id", "marketID", marketID)
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
isSupported, ok := domain.SupportedMarkets[marketID]
|
||||||
|
|
||||||
|
if !ok || !isSupported {
|
||||||
|
s.logger.Info("Unsupported market_id", "marketID", marketID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
marketRecord := domain.Market{
|
marketRecord := domain.Market{
|
||||||
EventID: eventID,
|
EventID: eventID,
|
||||||
FI: fi,
|
FI: fi,
|
||||||
|
|
@ -95,21 +254,18 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName
|
||||||
Odds: market.Odds,
|
Odds: market.Odds,
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = s.store.SaveNonLiveMarket(ctx, marketRecord)
|
err = s.store.SaveNonLiveMarket(ctx, marketRecord)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("failed to save market", "market_id", market.ID, "error", err)
|
||||||
|
errs = append(errs, fmt.Errorf("market %s: %w", market.ID, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
type OddsMarket struct {
|
if len(errs) > 0 {
|
||||||
ID json.Number `json:"id"`
|
return errors.Join(errs...)
|
||||||
Name string `json:"name"`
|
}
|
||||||
Odds []json.RawMessage `json:"odds"`
|
return nil
|
||||||
Header string `json:"header,omitempty"`
|
|
||||||
Handicap string `json:"handicap,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type OddsSection struct {
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
Sp map[string]OddsMarket `json:"sp"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceImpl) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) {
|
func (s *ServiceImpl) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) {
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,6 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
s.logger.Error("Sport ID is invalid", "event_id", outcome.EventID, "error", err)
|
s.logger.Error("Sport ID is invalid", "event_id", outcome.EventID, "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.fetchResult(ctx, outcome.EventID, outcome.OddID, outcome.MarketID, sportID, outcome)
|
result, err := s.fetchResult(ctx, outcome.EventID, outcome.OddID, outcome.MarketID, sportID, outcome)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to fetch result", "event_id", outcome.EventID, "error", err)
|
s.logger.Error("Failed to fetch result", "event_id", outcome.EventID, "error", err)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user