From 5331798009720210d33de88e1bb6c69d83d006aa Mon Sep 17 00:00:00 2001 From: Samuel Tariku Date: Wed, 13 Aug 2025 14:48:35 +0300 Subject: [PATCH] fix: disabling odds --- db/migrations/000001_fortune.up.sql | 19 ++++++ db/query/odds.sql | 6 +- gen/db/models.go | 1 + gen/db/odds.sql.go | 23 +++++-- internal/domain/log_fields.go | 17 +++++ internal/domain/odds.go | 2 + internal/repository/odds.go | 6 ++ internal/web_server/handlers/odd_handler.go | 75 +++++++-------------- 8 files changed, 90 insertions(+), 59 deletions(-) create mode 100644 internal/domain/log_fields.go diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index ddf5d79..d33c03a 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -265,6 +265,7 @@ CREATE TABLE events ( source TEXT DEFAULT 'b365api', is_featured BOOLEAN NOT NULL DEFAULT FALSE, is_monitored BOOLEAN NOT NULL DEFAULT FALSE, + winning_upper_limit INT NOT NULL, is_active BOOLEAN NOT NULL DEFAULT TRUE ); CREATE TABLE event_history ( @@ -288,6 +289,7 @@ CREATE TABLE odds ( category TEXT, raw_odds JSONB, fetched_at TIMESTAMP DEFAULT now(), + expires_at TIMESTAMP NOT NULL, source TEXT DEFAULT 'b365api', is_active BOOLEAN DEFAULT true, UNIQUE (market_id, name, handicap), @@ -303,6 +305,23 @@ CREATE TABLE odd_history ( odd_value DOUBLE PRECISION NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); +CREATE TABLE custom_odd ( + id BIGSERIAL PRIMARY KEY, + odd_id BIGINT NOT NULL, + raw_odd_id BIGINT NOT NULL, + market_id TEXT NOT NULL, + event_id TEXT NOT NULL, + odd_value DOUBLE PRECISION NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +CREATE TABLE disabled_odd ( + id BIGSERIAL PRIMARY KEY, + odd_id BIGINT NOT NULL, + raw_odd_id BIGINT NOT NULL, + market_id TEXT NOT NULL, + event_id TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); CREATE TABLE result_log ( id BIGSERIAL PRIMARY KEY, status_not_finished_count INT NOT NULL, diff --git a/db/query/odds.sql b/db/query/odds.sql index c48097b..4699000 100644 --- a/db/query/odds.sql +++ b/db/query/odds.sql @@ -14,7 +14,8 @@ INSERT INTO odds ( raw_odds, is_active, source, - fetched_at + fetched_at, + expires_at ) VALUES ( $1, @@ -31,7 +32,8 @@ VALUES ( $12, $13, $14, - $15 + $15, + $16 ) ON CONFLICT (event_id, market_id) DO UPDATE SET odds_value = EXCLUDED.odds_value, diff --git a/gen/db/models.go b/gen/db/models.go index fb4c8c6..f9cc71c 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -351,6 +351,7 @@ type Odd struct { Category pgtype.Text `json:"category"` RawOdds []byte `json:"raw_odds"` FetchedAt pgtype.Timestamp `json:"fetched_at"` + ExpiresAt pgtype.Timestamp `json:"expires_at"` Source pgtype.Text `json:"source"` IsActive pgtype.Bool `json:"is_active"` } diff --git a/gen/db/odds.sql.go b/gen/db/odds.sql.go index f0aa0fa..2865054 100644 --- a/gen/db/odds.sql.go +++ b/gen/db/odds.sql.go @@ -22,7 +22,7 @@ func (q *Queries) DeleteOddsForEvent(ctx context.Context, fi pgtype.Text) error } const GetALLPrematchOdds = `-- name: GetALLPrematchOdds :many -SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, source, is_active +SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, expires_at, source, is_active FROM odds WHERE is_active = true AND source = 'bet365' @@ -52,6 +52,7 @@ func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]Odd, error) { &i.Category, &i.RawOdds, &i.FetchedAt, + &i.ExpiresAt, &i.Source, &i.IsActive, ); err != nil { @@ -66,7 +67,7 @@ func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]Odd, error) { } const GetOddsByMarketID = `-- name: GetOddsByMarketID :one -SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, source, is_active +SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, expires_at, source, is_active FROM odds WHERE market_id = $1 AND fi = $2 @@ -97,6 +98,7 @@ func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDPa &i.Category, &i.RawOdds, &i.FetchedAt, + &i.ExpiresAt, &i.Source, &i.IsActive, ) @@ -104,7 +106,7 @@ func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDPa } const GetPaginatedPrematchOddsByUpcomingID = `-- name: GetPaginatedPrematchOddsByUpcomingID :many -SELECT o.id, o.event_id, o.fi, o.market_type, o.market_name, o.market_category, o.market_id, o.name, o.handicap, o.odds_value, o.section, o.category, o.raw_odds, o.fetched_at, o.source, o.is_active +SELECT o.id, o.event_id, o.fi, o.market_type, o.market_name, o.market_category, o.market_id, o.name, o.handicap, o.odds_value, o.section, o.category, o.raw_odds, o.fetched_at, o.expires_at, o.source, o.is_active FROM odds o JOIN events e ON o.fi = e.id WHERE e.id = $1 @@ -145,6 +147,7 @@ func (q *Queries) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, arg &i.Category, &i.RawOdds, &i.FetchedAt, + &i.ExpiresAt, &i.Source, &i.IsActive, ); err != nil { @@ -159,7 +162,7 @@ func (q *Queries) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, arg } const GetPrematchOdds = `-- name: GetPrematchOdds :many -SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, source, is_active +SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, expires_at, source, is_active FROM odds WHERE is_active = true AND source = 'bet365' @@ -189,6 +192,7 @@ func (q *Queries) GetPrematchOdds(ctx context.Context) ([]Odd, error) { &i.Category, &i.RawOdds, &i.FetchedAt, + &i.ExpiresAt, &i.Source, &i.IsActive, ); err != nil { @@ -203,7 +207,7 @@ func (q *Queries) GetPrematchOdds(ctx context.Context) ([]Odd, error) { } const GetPrematchOddsByUpcomingID = `-- name: GetPrematchOddsByUpcomingID :many -SELECT o.id, o.event_id, o.fi, o.market_type, o.market_name, o.market_category, o.market_id, o.name, o.handicap, o.odds_value, o.section, o.category, o.raw_odds, o.fetched_at, o.source, o.is_active +SELECT o.id, o.event_id, o.fi, o.market_type, o.market_name, o.market_category, o.market_id, o.name, o.handicap, o.odds_value, o.section, o.category, o.raw_odds, o.fetched_at, o.expires_at, o.source, o.is_active FROM odds o JOIN events e ON o.fi = e.id WHERE e.id = $1 @@ -237,6 +241,7 @@ func (q *Queries) GetPrematchOddsByUpcomingID(ctx context.Context, id string) ([ &i.Category, &i.RawOdds, &i.FetchedAt, + &i.ExpiresAt, &i.Source, &i.IsActive, ); err != nil { @@ -266,7 +271,8 @@ INSERT INTO odds ( raw_odds, is_active, source, - fetched_at + fetched_at, + expires_at ) VALUES ( $1, @@ -283,7 +289,8 @@ VALUES ( $12, $13, $14, - $15 + $15, + $16 ) ON CONFLICT (event_id, market_id) DO UPDATE SET odds_value = EXCLUDED.odds_value, @@ -315,6 +322,7 @@ type InsertNonLiveOddParams struct { IsActive pgtype.Bool `json:"is_active"` Source pgtype.Text `json:"source"` FetchedAt pgtype.Timestamp `json:"fetched_at"` + ExpiresAt pgtype.Timestamp `json:"expires_at"` } func (q *Queries) InsertNonLiveOdd(ctx context.Context, arg InsertNonLiveOddParams) error { @@ -334,6 +342,7 @@ func (q *Queries) InsertNonLiveOdd(ctx context.Context, arg InsertNonLiveOddPara arg.IsActive, arg.Source, arg.FetchedAt, + arg.ExpiresAt, ) return err } diff --git a/internal/domain/log_fields.go b/internal/domain/log_fields.go new file mode 100644 index 0000000..7ce6b1e --- /dev/null +++ b/internal/domain/log_fields.go @@ -0,0 +1,17 @@ +package domain + +import ( + "time" + + "github.com/gofiber/fiber/v2" + "go.uber.org/zap" +) + +var BadRequestZapFields = []zap.Field{ + zap.Int("status_code", fiber.StatusBadRequest), + zap.Time("timestamp", time.Now()), +} +var InternalServerErrorZapFields = []zap.Field{ + zap.Int("status_code", fiber.StatusBadRequest), + zap.Time("timestamp", time.Now()), +} diff --git a/internal/domain/odds.go b/internal/domain/odds.go index bb1cdc5..79a015c 100644 --- a/internal/domain/odds.go +++ b/internal/domain/odds.go @@ -35,6 +35,7 @@ type Odd struct { Category string `json:"category"` RawOdds []json.RawMessage `json:"raw_odds"` FetchedAt time.Time `json:"fetched_at"` + ExpiresAt time.Time `json:"expires_at"` Source string `json:"source"` IsActive bool `json:"is_active"` } @@ -44,4 +45,5 @@ type RawOddsByMarketID struct { Handicap string `json:"handicap"` RawOdds []json.RawMessage `json:"raw_odds"` FetchedAt time.Time `json:"fetched_at"` + ExpiresAt time.Time `json:"expires_at"` } diff --git a/internal/repository/odds.go b/internal/repository/odds.go index 09f9b4c..ac68ff9 100644 --- a/internal/repository/odds.go +++ b/internal/repository/odds.go @@ -51,6 +51,7 @@ func (s *Store) SaveNonLiveMarket(ctx context.Context, m domain.Market) error { IsActive: pgtype.Bool{Bool: true, Valid: true}, Source: pgtype.Text{String: m.Source, Valid: true}, FetchedAt: pgtype.Timestamp{Time: time.Now(), Valid: true}, + ExpiresAt: pgtype.Timestamp{Time: (time.Now()).Add(time.Hour), Valid: true}, } err := s.queries.InsertNonLiveOdd(ctx, params) @@ -120,6 +121,7 @@ func (s *Store) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.O return rawOdds }(), FetchedAt: odd.FetchedAt.Time, + ExpiresAt: odd.ExpiresAt.Time, Source: odd.Source.String, IsActive: odd.IsActive.Bool, } @@ -157,6 +159,7 @@ func (s *Store) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) { return rawOdds }(), FetchedAt: row.FetchedAt.Time, + ExpiresAt: row.ExpiresAt.Time, Source: row.Source.String, IsActive: row.IsActive.Bool, } @@ -193,6 +196,7 @@ func (s *Store) GetRawOddsByMarketID(ctx context.Context, marketID string, upcom return converted }(), FetchedAt: odds.FetchedAt.Time, + ExpiresAt: odds.ExpiresAt.Time, }, nil } @@ -227,6 +231,7 @@ func (s *Store) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomi Category: odd.Category.String, RawOdds: rawOdds, FetchedAt: odd.FetchedAt.Time, + ExpiresAt: odd.ExpiresAt.Time, Source: odd.Source.String, IsActive: odd.IsActive.Bool, } @@ -264,6 +269,7 @@ func (s *Store) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID stri Category: odd.Category.String, RawOdds: rawOdds, FetchedAt: odd.FetchedAt.Time, + ExpiresAt: odd.ExpiresAt.Time, Source: odd.Source.String, IsActive: odd.IsActive.Bool, } diff --git a/internal/web_server/handlers/odd_handler.go b/internal/web_server/handlers/odd_handler.go index 291771e..3412499 100644 --- a/internal/web_server/handlers/odd_handler.go +++ b/internal/web_server/handlers/odd_handler.go @@ -1,12 +1,11 @@ package handlers import ( - "strconv" - "time" - + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" + "strconv" ) // GetALLPrematchOdds @@ -19,19 +18,15 @@ import ( // @Failure 500 {object} response.APIResponse // @Router /api/v1/odds [get] func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error { - odds, err := h.prematchSvc.GetALLPrematchOdds(c.Context()) if err != nil { - h.mongoLoggerSvc.Error("Failed to retrieve all prematch odds", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + logFields := append([]zap.Field{}, domain.InternalServerErrorZapFields...) + logFields = append(logFields, zap.Error(err)) + h.mongoLoggerSvc.Error("Failed to retrieve all prematch odds", logFields...) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } return response.WriteJSON(c, fiber.StatusOK, "All prematch odds retrieved successfully", odds, nil) - } // GetRawOddsByMarketID @@ -47,37 +42,28 @@ func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error { // @Failure 500 {object} response.APIResponse // @Router /api/v1/odds/upcoming/{upcoming_id}/market/{market_id} [get] func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error { + logFields := []zap.Field{ + zap.String("market_id", c.Params("market_id")), + zap.String("upcoming_id", c.Params("upcoming_id")), + } marketID := c.Params("market_id") if marketID == "" { - h.mongoLoggerSvc.Info("Missing market_id", - zap.String("market_id", marketID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) + h.mongoLoggerSvc.Info("Missing market_id", append(logFields, domain.BadRequestZapFields...)...) return fiber.NewError(fiber.StatusBadRequest, "Missing market_id") } upcomingID := c.Params("upcoming_id") if upcomingID == "" { - h.mongoLoggerSvc.Info("Missing upcoming_id", - zap.String("upcoming", upcomingID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) + h.mongoLoggerSvc.Info("Missing upcoming_id", append(logFields, domain.BadRequestZapFields...)...) return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id") } rawOdds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketID, upcomingID) if err != nil { // Lets turn this into a warn because this is constantly going off - h.mongoLoggerSvc.Warn("Failed to get raw odds by market ID", - zap.String("marketID", marketID), - zap.String("upcomingID", upcomingID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + logFields = append(logFields, zap.Error(err)) + h.mongoLoggerSvc.Warn("Failed to get raw odds by market ID", append(logFields, domain.InternalServerErrorZapFields...)...) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } @@ -98,47 +84,36 @@ func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error { // @Failure 500 {object} response.APIResponse // @Router /api/v1/odds/upcoming/{upcoming_id} [get] func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error { + logFields := []zap.Field{ + zap.String("upcoming_id", c.Params("upcoming_id")), + zap.String("limit_param", c.Query("limit", "10")), + zap.String("offset_param", c.Query("offset", "0")), + } upcomingID := c.Params("upcoming_id") if upcomingID == "" { - h.mongoLoggerSvc.Info("Missing upcoming_id", - zap.String("upcoming", upcomingID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) + h.mongoLoggerSvc.Info("Missing upcoming_id", append(logFields, domain.BadRequestZapFields...)...) return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id") } limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10 if err != nil || limit <= 0 { - h.mongoLoggerSvc.Info("Invalid limit value", - zap.Int("limit", limit), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + logFields = append(logFields, zap.Error(err)) + h.mongoLoggerSvc.Info("Invalid limit value", append(logFields, domain.BadRequestZapFields...)...) return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value") } offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0 if err != nil || offset < 0 { - h.mongoLoggerSvc.Info("Invalid offset value", - zap.Int("offset", offset), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + logFields = append(logFields, zap.Error(err)) + h.mongoLoggerSvc.Info("Invalid offset value", append(logFields, domain.BadRequestZapFields...)...) return fiber.NewError(fiber.StatusBadRequest, err.Error()) } odds, err := h.prematchSvc.GetPrematchOddsByUpcomingID(c.Context(), upcomingID) if err != nil { - h.mongoLoggerSvc.Error("Failed to retrieve prematch odds", - zap.String("upcoming", upcomingID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + logFields = append(logFields, zap.Error(err)) + h.mongoLoggerSvc.Error("Failed to retrieve prematch odds", append(logFields, domain.InternalServerErrorZapFields...)...) return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve prematch odds"+err.Error()) }