diff --git a/cmd/main.go b/cmd/main.go index 6b945d5..584e821 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -67,7 +67,7 @@ func main() { app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{ JwtAccessKey: cfg.JwtKey, JwtAccessExpiry: cfg.AccessExpiry, - }, userSvc) + }, userSvc, oddsSvc) logger.Info("Starting server", "port", cfg.Port) if err := app.Run(); err != nil { diff --git a/db/query/odds.sql b/db/query/odds.sql index acb69d6..07e1c99 100644 --- a/db/query/odds.sql +++ b/db/query/odds.sql @@ -35,6 +35,25 @@ ON CONFLICT (event_id, market_id, header, name, handicap) DO UPDATE SET raw_event_id = EXCLUDED.raw_event_id; --- name: GetUpcomingEventIDs :many -SELECT id FROM events -WHERE is_live = false; +-- name: GetPrematchOdds :many +SELECT + id, + event_id, + fi, + raw_event_id, + market_type, + market_name, + market_category, + market_id, + header, + name, + handicap, + odds_value, + section, + category, + raw_odds, + fetched_at, + source, + is_active +FROM odds +WHERE event_id = $1 AND is_active = true AND source = 'b365api'; \ No newline at end of file diff --git a/docs/docs.go b/docs/docs.go index 6625028..6448575 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -180,6 +180,53 @@ const docTemplate = `{ } } }, + "/prematch/odds/{event_id}": { + "get": { + "description": "Retrieve prematch odds for a specific event by event ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "prematch" + ], + "summary": "Retrieve prematch odds for an event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "event_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.Odd" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + } + } + } + }, "/user/checkPhoneEmailExist": { "post": { "description": "Check if phone number or email exist", @@ -452,20 +499,80 @@ const docTemplate = `{ } }, "definitions": { + "domain.Odd": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "event_id": { + "type": "string" + }, + "fetched_at": { + "type": "string" + }, + "fi": { + "type": "string" + }, + "handicap": { + "type": "string" + }, + "header": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "market_category": { + "type": "string" + }, + "market_id": { + "type": "string" + }, + "market_name": { + "type": "string" + }, + "market_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "odds_value": { + "type": "number" + }, + "raw_event_id": { + "type": "string" + }, + "raw_odds": { + "type": "array", + "items": {} + }, + "section": { + "type": "string" + }, + "source": { + "type": "string" + } + } + }, "domain.Role": { "type": "string", "enum": [ - "admin", - "customer", "super_admin", + "admin", "branch_manager", + "customer", "cashier" ], "x-enum-varnames": [ - "RoleAdmin", - "RoleCustomer", "RoleSuperAdmin", + "RoleAdmin", "RoleBranchManager", + "RoleCustomer", "RoleCashier" ] }, diff --git a/docs/swagger.json b/docs/swagger.json index 76ae6c5..414256f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -172,6 +172,53 @@ } } }, + "/prematch/odds/{event_id}": { + "get": { + "description": "Retrieve prematch odds for a specific event by event ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "prematch" + ], + "summary": "Retrieve prematch odds for an event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "event_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.Odd" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + } + } + } + }, "/user/checkPhoneEmailExist": { "post": { "description": "Check if phone number or email exist", @@ -444,20 +491,80 @@ } }, "definitions": { + "domain.Odd": { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "event_id": { + "type": "string" + }, + "fetched_at": { + "type": "string" + }, + "fi": { + "type": "string" + }, + "handicap": { + "type": "string" + }, + "header": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "market_category": { + "type": "string" + }, + "market_id": { + "type": "string" + }, + "market_name": { + "type": "string" + }, + "market_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "odds_value": { + "type": "number" + }, + "raw_event_id": { + "type": "string" + }, + "raw_odds": { + "type": "array", + "items": {} + }, + "section": { + "type": "string" + }, + "source": { + "type": "string" + } + } + }, "domain.Role": { "type": "string", "enum": [ - "admin", - "customer", "super_admin", + "admin", "branch_manager", + "customer", "cashier" ], "x-enum-varnames": [ - "RoleAdmin", - "RoleCustomer", "RoleSuperAdmin", + "RoleAdmin", "RoleBranchManager", + "RoleCustomer", "RoleCashier" ] }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 166d41d..00333d0 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,17 +1,57 @@ definitions: + domain.Odd: + properties: + category: + type: string + event_id: + type: string + fetched_at: + type: string + fi: + type: string + handicap: + type: string + header: + type: string + id: + type: integer + is_active: + type: boolean + market_category: + type: string + market_id: + type: string + market_name: + type: string + market_type: + type: string + name: + type: string + odds_value: + type: number + raw_event_id: + type: string + raw_odds: + items: {} + type: array + section: + type: string + source: + type: string + type: object domain.Role: enum: - - admin - - customer - super_admin + - admin - branch_manager + - customer - cashier type: string x-enum-varnames: - - RoleAdmin - - RoleCustomer - RoleSuperAdmin + - RoleAdmin - RoleBranchManager + - RoleCustomer - RoleCashier handlers.CheckPhoneEmailExistReq: properties: @@ -275,6 +315,37 @@ paths: summary: Refresh token tags: - auth + /prematch/odds/{event_id}: + get: + consumes: + - application/json + description: Retrieve prematch odds for a specific event by event ID + parameters: + - description: Event ID + in: path + name: event_id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/domain.Odd' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.APIResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.APIResponse' + summary: Retrieve prematch odds for an event + tags: + - prematch /user/checkPhoneEmailExist: post: consumes: diff --git a/gen/db/odds.sql.go b/gen/db/odds.sql.go index f3e0029..003be80 100644 --- a/gen/db/odds.sql.go +++ b/gen/db/odds.sql.go @@ -11,24 +11,62 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -const GetUpcomingEventIDs = `-- name: GetUpcomingEventIDs :many -SELECT id FROM events -WHERE is_live = false +const GetPrematchOdds = `-- name: GetPrematchOdds :many +SELECT + id, + event_id, + fi, + raw_event_id, + market_type, + market_name, + market_category, + market_id, + header, + name, + handicap, + odds_value, + section, + category, + raw_odds, + fetched_at, + source, + is_active +FROM odds +WHERE event_id = $1 AND is_active = true AND source = 'b365api' ` -func (q *Queries) GetUpcomingEventIDs(ctx context.Context) ([]string, error) { - rows, err := q.db.Query(ctx, GetUpcomingEventIDs) +func (q *Queries) GetPrematchOdds(ctx context.Context, eventID pgtype.Text) ([]Odd, error) { + rows, err := q.db.Query(ctx, GetPrematchOdds, eventID) if err != nil { return nil, err } defer rows.Close() - var items []string + var items []Odd for rows.Next() { - var id string - if err := rows.Scan(&id); err != nil { + var i Odd + if err := rows.Scan( + &i.ID, + &i.EventID, + &i.Fi, + &i.RawEventID, + &i.MarketType, + &i.MarketName, + &i.MarketCategory, + &i.MarketID, + &i.Header, + &i.Name, + &i.Handicap, + &i.OddsValue, + &i.Section, + &i.Category, + &i.RawOdds, + &i.FetchedAt, + &i.Source, + &i.IsActive, + ); err != nil { return nil, err } - items = append(items, id) + items = append(items, i) } if err := rows.Err(); err != nil { return nil, err diff --git a/internal/domain/odds.go b/internal/domain/odds.go index 521fdb5..418b33d 100644 --- a/internal/domain/odds.go +++ b/internal/domain/odds.go @@ -3,7 +3,9 @@ package domain import ( "encoding/json" "time" + ) +type RawMessage interface{} // Change from json.RawMessage to interface{} type Market struct { EventID string @@ -15,9 +17,29 @@ type Market struct { UpdatedAt time.Time Odds []json.RawMessage - // Optional breakdown (extracted from odds) - Header string // only if processing one odd at a time + Header string Name string Handicap string OddsVal float64 } + +type Odd struct { + ID int64 `json:"id"` + EventID string `json:"event_id"` + Fi string `json:"fi"` + RawEventID string `json:"raw_event_id"` + MarketType string `json:"market_type"` + MarketName string `json:"market_name"` + MarketCategory string `json:"market_category"` + MarketID string `json:"market_id"` + Header string `json:"header"` + Name string `json:"name"` + Handicap string `json:"handicap"` + OddsValue float64 `json:"odds_value"` + Section string `json:"section"` + Category string `json:"category"` + RawOdds []RawMessage `json:"raw_odds"` + FetchedAt time.Time `json:"fetched_at"` + Source string `json:"source"` + IsActive bool `json:"is_active"` +} \ No newline at end of file diff --git a/internal/repository/odds.go b/internal/repository/odds.go index 399c13b..a21c729 100644 --- a/internal/repository/odds.go +++ b/internal/repository/odds.go @@ -15,14 +15,14 @@ import ( func (s *Store) SaveNonLiveMarket(ctx context.Context, m domain.Market) error { if len(m.Odds) == 0 { - fmt.Printf("⚠️ Market has no odds: %s (%s)\n", m.MarketType, m.EventID) + fmt.Printf(" Market has no odds: %s (%s)\n", m.MarketType, m.EventID) return nil } for _, raw := range m.Odds { var item map[string]interface{} if err := json.Unmarshal(raw, &item); err != nil { - fmt.Printf("❌ Invalid odd JSON for %s (%s): %v\n", m.MarketType, m.EventID, err) + fmt.Printf(" Invalid odd JSON for %s (%s): %v\n", m.MarketType, m.EventID, err) continue } @@ -31,7 +31,6 @@ func (s *Store) SaveNonLiveMarket(ctx context.Context, m domain.Market) error { handicap := getString(item["handicap"]) oddsVal := getFloat(item["odds"]) - // Marshal the full list of odds for reference (if needed) rawOddsBytes, _ := json.Marshal(m.Odds) params := dbgen.InsertNonLiveOddParams{ @@ -53,12 +52,12 @@ func (s *Store) SaveNonLiveMarket(ctx context.Context, m domain.Market) error { err := s.queries.InsertNonLiveOdd(ctx, params) if err != nil { - fmt.Printf("❌ Failed to insert odd for market %s (%s): %v\n", m.MarketType, m.EventID, err) + fmt.Printf(" Failed to insert odd for market %s (%s): %v\n", m.MarketType, m.EventID, err) _ = writeFailedMarketLog(m, err) continue } - fmt.Printf("✅ Inserted odd: %s | type=%s | header=%s | name=%s\n", m.EventID, m.MarketType, header, name) + fmt.Printf("Inserted odd: %s | type=%s | header=%s | name=%s\n", m.EventID, m.MarketType, header, name) } return nil } @@ -110,6 +109,43 @@ func getFloat(v interface{}) float64 { return 0 } -func (s *Store) GetUpcomingEventIDs(ctx context.Context) ([]string, error) { - return s.queries.GetUpcomingEventIDs(ctx) -} +func (s *Store) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { + eventIDParam := pgtype.Text{String: eventID, Valid: eventID != ""} + + odds, err := s.queries.GetPrematchOdds(ctx, eventIDParam) + if err != nil { + return nil, err + } + + domainOdds := make([]domain.Odd, len(odds)) + for i, odd := range odds { + domainOdds[i] = domain.Odd{ + ID: int64(odd.ID), // Cast int32 to int64 + EventID: odd.EventID.String, // Extract the String value + Fi: odd.Fi.String, // Extract the String value + RawEventID: odd.RawEventID.String, // Extract the String value + MarketType: odd.MarketType, // Direct assignment + MarketName: odd.MarketName.String, // Extract the String value + MarketCategory: odd.MarketCategory.String, // Extract the String value + MarketID: odd.MarketID.String, // Extract the String value + Header: odd.Header.String, // Extract the String value + Name: odd.Name.String, // Extract the String value + Handicap: odd.Handicap.String, // Extract the String value + OddsValue: odd.OddsValue.Float64, // Extract the Float64 value + Section: odd.Section, // Direct assignment + Category: odd.Category.String, // Extract the String value + RawOdds: func() []domain.RawMessage { + var rawOdds []domain.RawMessage + if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil { + rawOdds = nil + } + return rawOdds + }(), + FetchedAt: odd.FetchedAt.Time, // Extract the Time value + Source: odd.Source.String, // Extract the String value + IsActive: odd.IsActive.Bool, // Extract the Bool value + } + } + + return domainOdds, nil +} \ No newline at end of file diff --git a/internal/services/odds/port.go b/internal/services/odds/port.go index d95367e..d50a8af 100644 --- a/internal/services/odds/port.go +++ b/internal/services/odds/port.go @@ -1,8 +1,14 @@ package odds -import "context" +import ( + "context" + + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +) type Service interface { FetchNonLiveOdds(ctx context.Context) error + GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) + } diff --git a/internal/services/odds/service.go b/internal/services/odds/service.go index b45152c..2f3245d 100644 --- a/internal/services/odds/service.go +++ b/internal/services/odds/service.go @@ -23,13 +23,13 @@ func New(token string, store *repository.Store) *ServiceImpl { } func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error { - fmt.Println("🔄 Starting FetchNonLiveOdds...") + fmt.Println("Starting FetchNonLiveOdds...") sportID := 1 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: %v\n", err) + fmt.Printf("Failed to fetch upcoming: %v\n", err) return err } defer resp.Body.Close() @@ -42,23 +42,23 @@ func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error { } `json:"results"` } if err := json.Unmarshal(body, &upcomingData); err != nil || upcomingData.Success != 1 { - fmt.Printf("❌ Failed to decode upcoming response\nRaw: %s\n", string(body)) + fmt.Printf("Failed to decode upcoming response\nRaw: %s\n", string(body)) return err } for _, ev := range upcomingData.Results { eventID := ev.ID - fmt.Printf("📦 Fetching prematch odds for event_id=%s\n", eventID) + fmt.Printf("Fetching prematch odds for event_id=%s\n", eventID) 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) + fmt.Printf(" Odds fetch failed for event_id=%s: %v\n", eventID, err) continue } defer oddsResp.Body.Close() oddsBody, _ := io.ReadAll(oddsResp.Body) - fmt.Printf("📩 Raw odds response for event_id=%s: %.300s...\n", eventID, string(oddsBody)) + fmt.Printf(" Raw odds response for event_id=%s: %.300s...\n", eventID, string(oddsBody)) var oddsData struct { Success int `json:"success"` @@ -69,7 +69,7 @@ func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error { } `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)) + fmt.Printf(" Failed odds decode for event_id=%s\nRaw: %s\n", eventID, string(oddsBody)) continue } @@ -79,23 +79,23 @@ func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error { finalID = result.FI } if finalID == "" { - fmt.Println("⚠️ Skipping event with missing final ID.") + fmt.Println(" Skipping event with missing final ID.") continue } fmt.Printf("🗂 Saving prematch odds for event_id=%s\n", finalID) s.storeSection(ctx, finalID, result.FI, "main", result.Main) - fmt.Printf("✅ Finished storing prematch odds for event_id=%s\n", finalID) + fmt.Printf(" Finished storing prematch odds for event_id=%s\n", finalID) } - fmt.Println("✅ All prematch odds fetched and stored.") + fmt.Println(" All prematch odds fetched and stored.") return nil } func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName string, section OddsSection) { - fmt.Printf("📂 Processing section '%s' for event_id=%s\n", sectionName, eventID) + fmt.Printf(" Processing section '%s' for event_id=%s\n", sectionName, eventID) if len(section.Sp) == 0 { - fmt.Printf("⚠️ No odds in section '%s' for event_id=%s\n", sectionName, eventID) + fmt.Printf(" No odds in section '%s' for event_id=%s\n", sectionName, eventID) return } @@ -103,9 +103,9 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName updatedAt := time.Unix(updatedAtUnix, 0) for marketType, market := range section.Sp { - fmt.Printf("🔍 Processing market: %s (%s)\n", marketType, market.ID) + fmt.Printf(" Processing market: %s (%s)\n", marketType, market.ID) if len(market.Odds) == 0 { - fmt.Printf("⚠️ Empty odds for marketType=%s in section=%s\n", marketType, sectionName) + fmt.Printf(" Empty odds for marketType=%s in section=%s\n", marketType, sectionName) continue } @@ -120,17 +120,16 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName Odds: market.Odds, } - fmt.Printf("📦 Saving market to DB: %s (%s)\n", marketType, market.ID) + fmt.Printf(" Saving market to DB: %s (%s)\n", marketType, market.ID) err := s.store.SaveNonLiveMarket(ctx, marketRecord) if err != nil { - fmt.Printf("❌ Save failed for market %s (%s): %v\n", marketType, eventID, err) + fmt.Printf(" Save failed for market %s (%s): %v\n", marketType, eventID, err) } else { - fmt.Printf("✅ Successfully stored market: %s (%s)\n", marketType, eventID) + fmt.Printf(" Successfully stored market: %s (%s)\n", marketType, eventID) } } } -// Odds structures type OddsMarket struct { ID string `json:"id"` @@ -145,10 +144,22 @@ type OddsSection struct { Sp map[string]OddsMarket `json:"sp"` } -// Utility func getString(v interface{}) string { if str, ok := v.(string); ok { return str } return "" -} \ No newline at end of file +} + +func (s *ServiceImpl) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { + fmt.Printf("Retrieving prematch odds for event_id=%s\n", eventID) + + odds, err := s.store.GetPrematchOdds(ctx, eventID) + if err != nil { + fmt.Printf(" Failed to retrieve odds for event_id=%s: %v\n", eventID, err) + return nil, err + } + + fmt.Printf(" Retrieved %d odds entries for event_id=%s\n", len(odds), eventID) + return odds, nil +} diff --git a/internal/services/user/service.go b/internal/services/user/service.go index cfa93fd..17a7820 100644 --- a/internal/services/user/service.go +++ b/internal/services/user/service.go @@ -13,6 +13,7 @@ type Service struct { otpStore OtpStore smsGateway SmsGateway emailGateway EmailGateway + } func NewService( diff --git a/internal/web_server/app.go b/internal/web_server/app.go index 2ebd22e..4149f6b 100644 --- a/internal/web_server/app.go +++ b/internal/web_server/app.go @@ -1,55 +1,60 @@ package httpserver import ( - "fmt" - "log/slog" + "fmt" + "log/slog" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" - jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" - customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator" - "github.com/bytedance/sonic" - "github.com/gofiber/fiber/v2" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" + jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" + customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator" + + "github.com/bytedance/sonic" + "github.com/gofiber/fiber/v2" ) type App struct { - fiber *fiber.App - logger *slog.Logger - port int - authSvc *authentication.Service - userSvc *user.Service - validator *customvalidator.CustomValidator - JwtConfig jwtutil.JwtConfig + fiber *fiber.App + logger *slog.Logger + port int + authSvc *authentication.Service + userSvc *user.Service + validator *customvalidator.CustomValidator + JwtConfig jwtutil.JwtConfig + prematchSvc *odds.ServiceImpl } func NewApp( - port int, validator *customvalidator.CustomValidator, - authSvc *authentication.Service, - logger *slog.Logger, - JwtConfig jwtutil.JwtConfig, - userSvc *user.Service, + port int, validator *customvalidator.CustomValidator, + authSvc *authentication.Service, + logger *slog.Logger, + JwtConfig jwtutil.JwtConfig, + userSvc *user.Service, + prematchSvc *odds.ServiceImpl, ) *App { - app := fiber.New(fiber.Config{ - CaseSensitive: true, - DisableHeaderNormalizing: true, - JSONEncoder: sonic.Marshal, - JSONDecoder: sonic.Unmarshal, - }) - s := &App{ - fiber: app, - port: port, - authSvc: authSvc, - validator: validator, - logger: logger, - JwtConfig: JwtConfig, - userSvc: userSvc, - } + app := fiber.New(fiber.Config{ + CaseSensitive: true, + DisableHeaderNormalizing: true, + JSONEncoder: sonic.Marshal, + JSONDecoder: sonic.Unmarshal, + }) + s := &App{ + fiber: app, + port: port, + authSvc: authSvc, + validator: validator, + logger: logger, + JwtConfig: JwtConfig, + userSvc: userSvc, + prematchSvc: prematchSvc, + } - s.initAppRoutes() + s.initAppRoutes() - return s + return s } func (a *App) Run() error { - return a.fiber.Listen(fmt.Sprintf(":%d", a.port)) -} + return a.fiber.Listen(fmt.Sprintf(":%d", a.port)) +} \ No newline at end of file diff --git a/internal/web_server/cron.go b/internal/web_server/cron.go index e895b6e..68ddcf2 100644 --- a/internal/web_server/cron.go +++ b/internal/web_server/cron.go @@ -24,14 +24,14 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S } }, }, - // { - // spec: "*/5 * * * * *", // Every 5 seconds - // task: func() { - // if err := eventService.FetchLiveEvents(context.Background()); err != nil { - // log.Printf(" FetchLiveEvents error: %v", err) - // } - // }, - // }, + { + spec: "*/5 * * * * *", // Every 5 seconds + task: func() { + if err := eventService.FetchLiveEvents(context.Background()); err != nil { + log.Printf(" FetchLiveEvents error: %v", err) + } + }, + }, { spec: "*/5 * * * * *", // Every 5 seconds task: func() { diff --git a/internal/web_server/handlers/prematch.go b/internal/web_server/handlers/prematch.go new file mode 100644 index 0000000..cc48b10 --- /dev/null +++ b/internal/web_server/handlers/prematch.go @@ -0,0 +1,37 @@ +package handlers + +import ( + "github.com/gofiber/fiber/v2" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" + "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" + "log/slog" +) + +// GetPrematchOdds godoc +// @Summary Retrieve prematch odds for an event +// @Description Retrieve prematch odds for a specific event by event ID +// @Tags prematch +// @Accept json +// @Produce json +// @Param event_id path string true "Event ID" +// @Success 200 {array} domain.Odd +// @Failure 400 {object} response.APIResponse +// @Failure 500 {object} response.APIResponse +// @Router /prematch/odds/{event_id} [get] +func GetPrematchOdds(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Handler { + return func(c *fiber.Ctx) error { + eventID := c.Params("event_id") + if eventID == "" { + logger.Error("GetPrematchOdds failed: missing event_id") + return response.WriteJSON(c, fiber.StatusBadRequest, "Missing event_id", nil, nil) + } + + odds, err := prematchSvc.GetPrematchOdds(c.Context(), eventID) + if err != nil { + logger.Error("GetPrematchOdds failed", "error", err) + return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve odds", nil, nil) + } + + return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil) + } +} \ No newline at end of file diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index c30622d..22ebb93 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -26,6 +26,9 @@ func (a *App) initAppRoutes() { a.fiber.Post("/user/sendRegisterCode", handlers.SendRegisterCode(a.logger, a.userSvc, a.validator)) a.fiber.Post("/user/checkPhoneEmailExist", handlers.CheckPhoneEmailExist(a.logger, a.userSvc, a.validator)) a.fiber.Get("/user/profile", a.authMiddleware, handlers.UserProfile(a.logger, a.userSvc)) + + a.fiber.Get("/prematch/odds/:event_id", handlers.GetPrematchOdds(a.logger, a.prematchSvc)) + // Swagger a.fiber.Get("/swagger/*", fiberSwagger.WrapHandler) }