Yimaru-BackEnd/internal/services/odds/service.go
OneTap Technologies 1d6a533f7e addign odd
2025-04-11 13:57:32 +03:00

158 lines
4.3 KiB
Go

package odds
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"
"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 {
sportIDs := []int{1, 13, 78, 18, 91, 16, 17}
for _, sportID := range sportIDs {
upcomingURL := fmt.Sprintf("https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s", sportID, s.token)
resp, err := http.Get(upcomingURL)
if err != nil {
fmt.Printf("❌ Failed to fetch upcoming for sport_id=%d: %v\n", sportID, err)
continue
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data struct {
Success int `json:"success"`
Results []map[string]interface{} `json:"results"`
}
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
fmt.Printf("❌ Failed to decode upcoming for sport_id=%d\nRaw: %s\n", sportID, string(body))
continue
}
for _, ev := range data.Results {
if getString(ev["type"]) != "EV" {
continue
}
eventID := getString(ev["ID"])
if eventID == "" {
continue
}
prematchURL := fmt.Sprintf("https://api.b365api.com/v3/bet365/prematch?token=%s&FI=%s", s.token, eventID)
oddsResp, err := http.Get(prematchURL)
if err != nil {
fmt.Printf("❌ Odds fetch failed for event_id=%s: %v\n", eventID, err)
continue
}
defer oddsResp.Body.Close()
oddsBody, _ := io.ReadAll(oddsResp.Body)
var oddsData 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"`
}
if err := json.Unmarshal(oddsBody, &oddsData); err != nil || oddsData.Success != 1 || len(oddsData.Results) == 0 {
fmt.Printf("❌ Failed odds decode for event_id=%s\nRaw: %s\n", eventID, string(oddsBody))
continue
}
result := oddsData.Results[0]
finalID := result.EventID
if finalID == "" {
finalID = result.FI
}
if finalID == "" {
fmt.Println("⚠️ Skipping event with missing final ID.")
continue
}
saveOdds := func(sectionName string, section OddsSection) error {
if len(section.Sp) == 0 {
fmt.Printf("⚠️ No odds in section '%s' for event_id=%s\n", sectionName, finalID)
return nil
}
updatedAtUnix, _ := strconv.ParseInt(section.UpdatedAt, 10, 64)
updatedAt := time.Unix(updatedAtUnix, 0)
for marketType, market := range section.Sp {
if len(market.Odds) == 0 {
fmt.Printf("⚠️ No odds for marketType=%s in section=%s\n", marketType, sectionName)
continue
}
marketRecord := domain.Market{
EventID: finalID,
FI: result.FI,
MarketCategory: sectionName,
MarketType: marketType,
MarketName: market.Name,
MarketID: market.ID,
UpdatedAt: updatedAt,
Odds: market.Odds,
}
s.store.SaveNonLiveOdd(ctx, marketRecord)
fmt.Printf("✅ STORED MARKET: event_id=%s | type=%s | name=%s\n", finalID, marketType, market.Name)
}
return nil
}
if err := saveOdds("asian_lines", result.AsianLines); err != nil {
fmt.Printf("⚠️ Skipping event %s due to asian_lines error: %v\n", finalID, err)
continue
}
if err := saveOdds("goals", result.Goals); err != nil {
fmt.Printf("⚠️ Skipping event %s due to goals error: %v\n", finalID, err)
continue
}
fmt.Printf("✅ Done storing all odds for event_id=%s\n", finalID)
}
}
fmt.Println("✅ All non-live odds fetched and stored.")
return nil
}
// Odds structures
type OddsMarket struct {
ID string `json:"id"`
Name string `json:"name"`
Odds []json.RawMessage `json:"odds"`
}
type OddsSection struct {
UpdatedAt string `json:"updated_at"`
Sp map[string]OddsMarket `json:"sp"`
}
// Helper
func getString(v interface{}) string {
if str, ok := v.(string); ok {
return str
}
return ""
}