153 lines
4.0 KiB
Go
153 lines
4.0 KiB
Go
package odds
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
|
)
|
|
|
|
type ServiceImpl struct {
|
|
token string
|
|
store *repository.Store
|
|
}
|
|
|
|
func New(token string, store *repository.Store) *ServiceImpl {
|
|
return &ServiceImpl{token: token, store: store}
|
|
}
|
|
func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error {
|
|
eventIDs, err := s.store.GetUpcomingEventIDs(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("fetch upcoming event IDs: %w", err)
|
|
}
|
|
|
|
type OddsMarket struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Odds []struct {
|
|
ID string `json:"id"`
|
|
Odds string `json:"odds"`
|
|
Header string `json:"header,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Handicap string `json:"handicap,omitempty"`
|
|
} `json:"odds"`
|
|
}
|
|
|
|
type OddsSection struct {
|
|
UpdatedAt string `json:"updated_at"`
|
|
Sp map[string]OddsMarket `json:"sp"`
|
|
}
|
|
|
|
type StructuredOddsResponse struct {
|
|
Success int `json:"success"`
|
|
Results []struct {
|
|
EventID string `json:"event_id"`
|
|
FI string `json:"FI"`
|
|
AsianLines OddsSection `json:"asian_lines"`
|
|
Goals OddsSection `json:"goals"`
|
|
} `json:"results"`
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
sem := make(chan struct{}, 5)
|
|
|
|
for _, eventID := range eventIDs {
|
|
if eventID == "" || len(eventID) < 5 {
|
|
continue
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func(originalID string) {
|
|
defer wg.Done()
|
|
sem <- struct{}{}
|
|
defer func() { <-sem }()
|
|
|
|
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/odds?token=%s&event_id=%s", s.token, originalID)
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
fmt.Printf(" Failed HTTP request for event_id=%s: %v\n", originalID, err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
fmt.Printf(" Failed to read response body for event_id=%s: %v\n", originalID, err)
|
|
return
|
|
}
|
|
|
|
var data StructuredOddsResponse
|
|
if err := json.Unmarshal(body, &data); err != nil {
|
|
fmt.Printf(" JSON unmarshal failed for event_id=%s. Response: %s\n", originalID, string(body))
|
|
return
|
|
}
|
|
|
|
if data.Success != 1 || len(data.Results) == 0 {
|
|
fmt.Printf(" API response error or no results for event_id=%s\nBody: %s\n", originalID, string(body))
|
|
return
|
|
}
|
|
|
|
result := data.Results[0]
|
|
finalEventID := result.EventID
|
|
if finalEventID == "" {
|
|
finalEventID = result.FI
|
|
}
|
|
if finalEventID == "" {
|
|
fmt.Printf(" Skipping event_id=%s due to missing both event_id and FI\n", originalID)
|
|
return
|
|
}
|
|
|
|
saveOdds := func(sectionName string, section OddsSection) {
|
|
for marketType, market := range section.Sp {
|
|
for _, odd := range market.Odds {
|
|
val, err := strconv.ParseFloat(odd.Odds, 64)
|
|
if err != nil {
|
|
fmt.Printf(" Skipping invalid odds for market=%s, event_id=%s\n", marketType, finalEventID)
|
|
continue
|
|
}
|
|
|
|
record := domain.OddsRecord{
|
|
EventID: finalEventID,
|
|
MarketType: marketType,
|
|
Header: odd.Header,
|
|
Name: odd.Name,
|
|
Handicap: odd.Handicap,
|
|
OddsValue: val,
|
|
Section: sectionName,
|
|
Category: market.ID,
|
|
MarketID: odd.ID,
|
|
RawEventID: originalID,
|
|
}
|
|
|
|
fmt.Printf("🟡 Preparing to store: event_id=%s | market=%s | header=%s | name=%s | odds=%.3f\n",
|
|
finalEventID, marketType, odd.Header, odd.Name, val)
|
|
|
|
err = s.store.SaveNonLiveOdd(ctx, record)
|
|
if err != nil {
|
|
fmt.Printf("❌ DB save error for market=%s, event_id=%s: %v\nRecord: %+v\n", marketType, finalEventID, err, record)
|
|
} else {
|
|
fmt.Printf("✅ Stored odd: event_id=%s | market=%s | odds=%.3f\n", finalEventID, marketType, val)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
saveOdds("asian_lines", result.AsianLines)
|
|
saveOdds("goals", result.Goals)
|
|
|
|
}(eventID)
|
|
}
|
|
|
|
wg.Wait()
|
|
fmt.Println("✅ All non-live odds fetched and stored.")
|
|
return nil
|
|
}
|
|
|