diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index 9e7035b..fb57714 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -322,7 +322,6 @@ 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 @@ -331,7 +330,6 @@ 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 ); diff --git a/db/query/custom_odds.sql b/db/query/custom_odds.sql new file mode 100644 index 0000000..199cda1 --- /dev/null +++ b/db/query/custom_odds.sql @@ -0,0 +1,26 @@ +-- name: InsertCustomOdd :one +INSERT INTO custom_odd ( + odd_id, + raw_odd_id, + event_id, + odd_value + ) +VALUES ($1, $2, $3, $4) +RETURNING *; +-- name: GetAllCustomOdds :many +SELECT * +FROM custom_odd; +-- name: GetCustomOddByRawOddID :one +SELECT * +FROM custom_odd +WHERE raw_odd_id = $1; +-- name: GetCustomOddByID :one +SELECT * +FROM custom_odd +WHERE id = $1; +-- name: DeleteCustomOddsByID :exec +DELETE FROM disabled_odd +WHERE raw_odd_id = $1; +-- name: DeleteCustomOddsByRawOddID :exec +DELETE FROM disabled_odd +WHERE raw_odd_id = $1; \ No newline at end of file diff --git a/db/query/disabled_odds.sql b/db/query/disabled_odds.sql new file mode 100644 index 0000000..9e328f1 --- /dev/null +++ b/db/query/disabled_odds.sql @@ -0,0 +1,25 @@ +-- name: InsertDisabledOdds :one +INSERT INTO disabled_odd ( + odd_id, + event_id, + raw_odd_id + ) +VALUES ($1, $2, $3) +RETURNING *; +-- name: GetAllDisabledOdds :many +SELECT * +FROM disabled_odd; +-- name: GetDisabledOddByRawOddID :one +SELECT * +FROM disabled_odd +WHERE raw_odd_id = $1; +-- name: GetDisabledOddByID :one +SELECT * +FROM disabled_odd +WHERE raw_odd_id = $1; +-- name: DeleteDisabledOddsByID :exec +DELETE FROM disabled_odd +WHERE raw_odd_id = $1; +-- name: DeleteDisabledOddsByRawOddID :exec +DELETE FROM disabled_odd +WHERE raw_odd_id = $1; \ No newline at end of file diff --git a/db/query/odd_history.sql b/db/query/odd_history.sql index fb9284f..f7368fc 100644 --- a/db/query/odd_history.sql +++ b/db/query/odd_history.sql @@ -35,9 +35,8 @@ WHERE ( created_at < sqlc.narg('created_after') OR sqlc.narg('created_after') IS NULL ); - -- name: GetInitialOddPerDay :many -SELECT DISTINCT ON (DATE_TRUNC('day', created_at)) * +SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) * FROM odd_history WHERE ( odd_id = sqlc.narg('odd_id') @@ -63,5 +62,5 @@ WHERE ( created_at < sqlc.narg('created_after') OR sqlc.narg('created_after') IS NULL ) -ORDER BY DATE_TRUNC('day', created_at), +ORDER BY DATE_TRUNC($1, created_at), created_at ASC; \ No newline at end of file diff --git a/gen/db/custom_odds.sql.go b/gen/db/custom_odds.sql.go new file mode 100644 index 0000000..9e84561 --- /dev/null +++ b/gen/db/custom_odds.sql.go @@ -0,0 +1,139 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 +// source: custom_odds.sql + +package dbgen + +import ( + "context" +) + +const DeleteCustomOddsByID = `-- name: DeleteCustomOddsByID :exec +DELETE FROM disabled_odd +WHERE raw_odd_id = $1 +` + +func (q *Queries) DeleteCustomOddsByID(ctx context.Context, rawOddID int64) error { + _, err := q.db.Exec(ctx, DeleteCustomOddsByID, rawOddID) + return err +} + +const DeleteCustomOddsByRawOddID = `-- name: DeleteCustomOddsByRawOddID :exec +DELETE FROM disabled_odd +WHERE raw_odd_id = $1 +` + +func (q *Queries) DeleteCustomOddsByRawOddID(ctx context.Context, rawOddID int64) error { + _, err := q.db.Exec(ctx, DeleteCustomOddsByRawOddID, rawOddID) + return err +} + +const GetAllCustomOdds = `-- name: GetAllCustomOdds :many +SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at +FROM custom_odd +` + +func (q *Queries) GetAllCustomOdds(ctx context.Context) ([]CustomOdd, error) { + rows, err := q.db.Query(ctx, GetAllCustomOdds) + if err != nil { + return nil, err + } + defer rows.Close() + var items []CustomOdd + for rows.Next() { + var i CustomOdd + if err := rows.Scan( + &i.ID, + &i.OddID, + &i.RawOddID, + &i.EventID, + &i.OddValue, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const GetCustomOddByID = `-- name: GetCustomOddByID :one +SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at +FROM custom_odd +WHERE id = $1 +` + +func (q *Queries) GetCustomOddByID(ctx context.Context, id int64) (CustomOdd, error) { + row := q.db.QueryRow(ctx, GetCustomOddByID, id) + var i CustomOdd + err := row.Scan( + &i.ID, + &i.OddID, + &i.RawOddID, + &i.EventID, + &i.OddValue, + &i.CreatedAt, + ) + return i, err +} + +const GetCustomOddByRawOddID = `-- name: GetCustomOddByRawOddID :one +SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at +FROM custom_odd +WHERE raw_odd_id = $1 +` + +func (q *Queries) GetCustomOddByRawOddID(ctx context.Context, rawOddID int64) (CustomOdd, error) { + row := q.db.QueryRow(ctx, GetCustomOddByRawOddID, rawOddID) + var i CustomOdd + err := row.Scan( + &i.ID, + &i.OddID, + &i.RawOddID, + &i.EventID, + &i.OddValue, + &i.CreatedAt, + ) + return i, err +} + +const InsertCustomOdd = `-- name: InsertCustomOdd :one +INSERT INTO custom_odd ( + odd_id, + raw_odd_id, + event_id, + odd_value + ) +VALUES ($1, $2, $3, $4) +RETURNING id, odd_id, raw_odd_id, event_id, odd_value, created_at +` + +type InsertCustomOddParams struct { + OddID int64 `json:"odd_id"` + RawOddID int64 `json:"raw_odd_id"` + EventID string `json:"event_id"` + OddValue float64 `json:"odd_value"` +} + +func (q *Queries) InsertCustomOdd(ctx context.Context, arg InsertCustomOddParams) (CustomOdd, error) { + row := q.db.QueryRow(ctx, InsertCustomOdd, + arg.OddID, + arg.RawOddID, + arg.EventID, + arg.OddValue, + ) + var i CustomOdd + err := row.Scan( + &i.ID, + &i.OddID, + &i.RawOddID, + &i.EventID, + &i.OddValue, + &i.CreatedAt, + ) + return i, err +} diff --git a/gen/db/disabled_odds.sql.go b/gen/db/disabled_odds.sql.go new file mode 100644 index 0000000..19fa041 --- /dev/null +++ b/gen/db/disabled_odds.sql.go @@ -0,0 +1,128 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 +// source: disabled_odds.sql + +package dbgen + +import ( + "context" +) + +const DeleteDisabledOddsByID = `-- name: DeleteDisabledOddsByID :exec +DELETE FROM disabled_odd +WHERE raw_odd_id = $1 +` + +func (q *Queries) DeleteDisabledOddsByID(ctx context.Context, rawOddID int64) error { + _, err := q.db.Exec(ctx, DeleteDisabledOddsByID, rawOddID) + return err +} + +const DeleteDisabledOddsByRawOddID = `-- name: DeleteDisabledOddsByRawOddID :exec +DELETE FROM disabled_odd +WHERE raw_odd_id = $1 +` + +func (q *Queries) DeleteDisabledOddsByRawOddID(ctx context.Context, rawOddID int64) error { + _, err := q.db.Exec(ctx, DeleteDisabledOddsByRawOddID, rawOddID) + return err +} + +const GetAllDisabledOdds = `-- name: GetAllDisabledOdds :many +SELECT id, odd_id, raw_odd_id, event_id, created_at +FROM disabled_odd +` + +func (q *Queries) GetAllDisabledOdds(ctx context.Context) ([]DisabledOdd, error) { + rows, err := q.db.Query(ctx, GetAllDisabledOdds) + if err != nil { + return nil, err + } + defer rows.Close() + var items []DisabledOdd + for rows.Next() { + var i DisabledOdd + if err := rows.Scan( + &i.ID, + &i.OddID, + &i.RawOddID, + &i.EventID, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const GetDisabledOddByID = `-- name: GetDisabledOddByID :one +SELECT id, odd_id, raw_odd_id, event_id, created_at +FROM disabled_odd +WHERE raw_odd_id = $1 +` + +func (q *Queries) GetDisabledOddByID(ctx context.Context, rawOddID int64) (DisabledOdd, error) { + row := q.db.QueryRow(ctx, GetDisabledOddByID, rawOddID) + var i DisabledOdd + err := row.Scan( + &i.ID, + &i.OddID, + &i.RawOddID, + &i.EventID, + &i.CreatedAt, + ) + return i, err +} + +const GetDisabledOddByRawOddID = `-- name: GetDisabledOddByRawOddID :one +SELECT id, odd_id, raw_odd_id, event_id, created_at +FROM disabled_odd +WHERE raw_odd_id = $1 +` + +func (q *Queries) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (DisabledOdd, error) { + row := q.db.QueryRow(ctx, GetDisabledOddByRawOddID, rawOddID) + var i DisabledOdd + err := row.Scan( + &i.ID, + &i.OddID, + &i.RawOddID, + &i.EventID, + &i.CreatedAt, + ) + return i, err +} + +const InsertDisabledOdds = `-- name: InsertDisabledOdds :one +INSERT INTO disabled_odd ( + odd_id, + event_id, + raw_odd_id + ) +VALUES ($1, $2, $3) +RETURNING id, odd_id, raw_odd_id, event_id, created_at +` + +type InsertDisabledOddsParams struct { + OddID int64 `json:"odd_id"` + EventID string `json:"event_id"` + RawOddID int64 `json:"raw_odd_id"` +} + +func (q *Queries) InsertDisabledOdds(ctx context.Context, arg InsertDisabledOddsParams) (DisabledOdd, error) { + row := q.db.QueryRow(ctx, InsertDisabledOdds, arg.OddID, arg.EventID, arg.RawOddID) + var i DisabledOdd + err := row.Scan( + &i.ID, + &i.OddID, + &i.RawOddID, + &i.EventID, + &i.CreatedAt, + ) + return i, err +} diff --git a/gen/db/models.go b/gen/db/models.go index 0392398..9b1a3fa 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -212,7 +212,6 @@ type CustomOdd struct { ID int64 `json:"id"` OddID int64 `json:"odd_id"` RawOddID int64 `json:"raw_odd_id"` - MarketID string `json:"market_id"` EventID string `json:"event_id"` OddValue float64 `json:"odd_value"` CreatedAt pgtype.Timestamp `json:"created_at"` @@ -262,7 +261,6 @@ type DisabledOdd struct { ID int64 `json:"id"` OddID int64 `json:"odd_id"` RawOddID int64 `json:"raw_odd_id"` - MarketID string `json:"market_id"` EventID string `json:"event_id"` CreatedAt pgtype.Timestamp `json:"created_at"` } diff --git a/gen/db/odd_history.sql.go b/gen/db/odd_history.sql.go index ecb35d4..fa5272a 100644 --- a/gen/db/odd_history.sql.go +++ b/gen/db/odd_history.sql.go @@ -85,37 +85,38 @@ func (q *Queries) GetAllOddHistory(ctx context.Context, arg GetAllOddHistoryPara } const GetInitialOddPerDay = `-- name: GetInitialOddPerDay :many -SELECT DISTINCT ON (DATE_TRUNC('day', created_at)) id, odd_id, raw_odd_id, market_id, event_id, odd_value, created_at +SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) id, odd_id, raw_odd_id, market_id, event_id, odd_value, created_at FROM odd_history WHERE ( - odd_id = $1 - OR $1 IS NULL - ) - AND ( - market_id = $2 + odd_id = $2 OR $2 IS NULL ) AND ( - raw_odd_id = $3 + market_id = $3 OR $3 IS NULL ) AND ( - event_id = $4 + raw_odd_id = $4 OR $4 IS NULL ) AND ( - created_at > $5 + event_id = $5 OR $5 IS NULL ) AND ( - created_at < $6 + created_at > $6 OR $6 IS NULL ) -ORDER BY DATE_TRUNC('day', created_at), + AND ( + created_at < $7 + OR $7 IS NULL + ) +ORDER BY DATE_TRUNC($1, created_at), created_at ASC ` type GetInitialOddPerDayParams struct { + DateTrunc string `json:"date_trunc"` OddID pgtype.Int8 `json:"odd_id"` MarketID pgtype.Text `json:"market_id"` RawOddID pgtype.Int8 `json:"raw_odd_id"` @@ -126,6 +127,7 @@ type GetInitialOddPerDayParams struct { func (q *Queries) GetInitialOddPerDay(ctx context.Context, arg GetInitialOddPerDayParams) ([]OddHistory, error) { rows, err := q.db.Query(ctx, GetInitialOddPerDay, + arg.DateTrunc, arg.OddID, arg.MarketID, arg.RawOddID, diff --git a/internal/domain/bet.go b/internal/domain/bet.go index bc6aae0..16e55b0 100644 --- a/internal/domain/bet.go +++ b/internal/domain/bet.go @@ -4,6 +4,9 @@ import ( "time" ) +// The Odd ID here is not the odd id from our database +// but the raw_odd_id from the betapi.from within the raw_odds json +// This can be refactor later type BetOutcome struct { ID int64 `json:"id" example:"1"` BetID int64 `json:"bet_id" example:"1"` diff --git a/internal/domain/common.go b/internal/domain/common.go index 93db311..dd5cc38 100644 --- a/internal/domain/common.go +++ b/internal/domain/common.go @@ -1,188 +1,11 @@ package domain import ( - "encoding/json" - "fmt" - "strconv" - "time" - - "github.com/jackc/pgx/v5/pgtype" "go.uber.org/zap" ) var MongoDBLogger *zap.Logger -type ValidInt64 struct { - Value int64 - Valid bool -} -type ValidInt struct { - Value int - Valid bool -} -type ValidInt32 struct { - Value int32 - Valid bool -} - -type ValidFloat32 struct { - Value float32 - Valid bool -} - -type ValidString struct { - Value string - Valid bool -} -type ValidTime struct { - Value time.Time - Valid bool -} -type ValidBool struct { - Value bool - Valid bool -} - -// ValidInt64 → pgtype.Int8 -func (v ValidInt64) ToPG() pgtype.Int8 { - return pgtype.Int8{ - Int64: v.Value, - Valid: v.Valid, - } -} - -// ValidInt32 → pgtype.Int4 -func (v ValidInt32) ToPG() pgtype.Int4 { - return pgtype.Int4{ - Int32: v.Value, - Valid: v.Valid, - } -} - -// ValidInt → pgtype.Int4 (Go int mapped to int32 for pg compatibility) -func (v ValidInt) ToPG() pgtype.Int4 { - return pgtype.Int4{ - Int32: int32(v.Value), - Valid: v.Valid, - } -} - -// ValidFloat32 → pgtype.Float4 -func (v ValidFloat32) ToPG() pgtype.Float4 { - return pgtype.Float4{ - Float32: v.Value, - Valid: v.Valid, - } -} - -// ValidString → pgtype.Text -func (v ValidString) ToPG() pgtype.Text { - return pgtype.Text{ - String: v.Value, - Valid: v.Valid, - } -} - -// ValidTime → pgtype.Timestamp -func (v ValidTime) ToPG() pgtype.Timestamp { - return pgtype.Timestamp{ - Time: v.Value, - Valid: v.Valid, - } -} - -// ValidBool → pgtype.Bool -func (v ValidBool) ToPG() pgtype.Bool { - return pgtype.Bool{ - Bool: v.Value, - Valid: v.Valid, - } -} - -func ConvertInt64Ptr(value *int64) ValidInt64 { - if value == nil { - return ValidInt64{} - } - return ValidInt64{ - Value: *value, - Valid: true, - } -} - -func ConvertInt32Ptr(value *int32) ValidInt32 { - if value == nil { - return ValidInt32{} - } - return ValidInt32{ - Value: *value, - Valid: true, - } -} - -func ConvertStringPtr(value *string) ValidString { - if value == nil { - return ValidString{} - } - return ValidString{ - Value: *value, - Valid: true, - } -} - -func ConvertFloat32Ptr(value *float32) ValidFloat32 { - if value == nil { - return ValidFloat32{} - } - return ValidFloat32{ - Value: *value, - Valid: true, - } -} - -func ConvertCurrencyFloatPtr(value *float32) ValidInt64 { - if value == nil { - return ValidInt64{} - } - - convertedCurrency := ToCurrency(*value) - - return ValidInt64{ - Value: int64(convertedCurrency), - Valid: true, - } -} - -func ConvertBoolPtr(value *bool) ValidBool { - if value == nil { - return ValidBool{} - } - return ValidBool{ - Value: *value, - Valid: true, - } -} - -type Currency int64 - -// ToCurrency converts a float32 to Currency -func ToCurrency(f float32) Currency { - return Currency((f * 100) + 0.5) -} - -// Float32 converts a Currency to float32 -func (m Currency) Float32() float32 { - x := float32(m) - x = x / 100 - return x -} - -// String returns a formatted Currency value -func (m Currency) String() string { - x := float32(m) - x = x / 100 - return fmt.Sprintf("$%.2f", x) -} - type ResponseWDataFactory[T any] struct { Data T `json:"data"` Response @@ -215,71 +38,3 @@ type Pagination struct { func PtrFloat64(v float64) *float64 { return &v } func PtrInt64(v int64) *int64 { return &v } - -type Int64JSON int64 - -// func (i *Int64JSON) Parse() (int64, error) { -// var asString string -// if err := json.Unmarshal(i, &asString); err == nil { -// // Try to parse the string to int64 -// return strconv.ParseInt(strings.TrimSpace(asString), 10, 64) -// } - -// var asInt int64 -// if err := json.Unmarshal(i, &asInt); err == nil { -// return asInt, nil -// } - -// // Neither string nor int worked -// return 0, fmt.Errorf("invalid int64 value: %s", string(i)) -// } - -func (i *Int64JSON) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err == nil { - // it was a string, parse it - v, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return err - } - *i = Int64JSON(v) - return nil - } - - var v int64 - if err := json.Unmarshal(data, &v); err == nil { - *i = Int64JSON(v) - return nil - } - - return fmt.Errorf("invalid int64 value: %s", string(data)) -} - -type NullableInt64JSON struct { - Int64 int64 - Valid bool -} - -func (n *NullableInt64JSON) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err == nil { - if s == "" { - n.Valid = false - return nil - } - v, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return err - } - n.Int64, n.Valid = v, true - return nil - } - - var v int64 - if err := json.Unmarshal(data, &v); err == nil { - n.Int64, n.Valid = v, true - return nil - } - - return fmt.Errorf("invalid int64 value: %s", string(data)) -} diff --git a/internal/domain/currency.go b/internal/domain/currency.go index 3a2eb49..e436a87 100644 --- a/internal/domain/currency.go +++ b/internal/domain/currency.go @@ -6,6 +6,27 @@ import ( "time" ) +type Currency int64 + +// ToCurrency converts a float32 to Currency +func ToCurrency(f float32) Currency { + return Currency((f * 100) + 0.5) +} + +// Float32 converts a Currency to float32 +func (m Currency) Float32() float32 { + x := float32(m) + x = x / 100 + return x +} + +// String returns a formatted Currency value +func (m Currency) String() string { + x := float32(m) + x = x / 100 + return fmt.Sprintf("$%.2f", x) +} + type IntCurrency string const ( diff --git a/internal/domain/custom_odds.go b/internal/domain/custom_odds.go new file mode 100644 index 0000000..6f9e919 --- /dev/null +++ b/internal/domain/custom_odds.go @@ -0,0 +1,43 @@ +package domain + +import ( + "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" +) + +type CustomOdd struct { + ID int64 + OddID int64 + RawOddID int64 + EventID string + OddValue float64 + CreatedAt time.Time +} + +type CreateCustomOdd struct { + OddID int64 + RawOddID int64 + EventID string + OddValue float64 +} + +func ConvertCreateCustomOdd(odd CreateCustomOdd) dbgen.InsertCustomOddParams { + return dbgen.InsertCustomOddParams{ + OddID: odd.OddID, + RawOddID: odd.RawOddID, + EventID: odd.EventID, + OddValue: odd.OddValue, + } +} + +func ConvertDBCustomOdd(dbCustomOdd dbgen.CustomOdd) CustomOdd { + return CustomOdd{ + ID: dbCustomOdd.ID, + OddID: dbCustomOdd.OddID, + RawOddID: dbCustomOdd.RawOddID, + EventID: dbCustomOdd.EventID, + OddValue: dbCustomOdd.OddValue, + CreatedAt: dbCustomOdd.CreatedAt.Time, + } +} diff --git a/internal/domain/disabled_odds.go b/internal/domain/disabled_odds.go new file mode 100644 index 0000000..d05b3fc --- /dev/null +++ b/internal/domain/disabled_odds.go @@ -0,0 +1,47 @@ +package domain + +import ( + "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" +) + +type DisabledOdd struct { + ID int64 + OddID int64 + RawOddID int64 + EventID string + CreatedAt time.Time +} + +type CreateDisabledOdd struct { + OddID int64 + RawOddID int64 + EventID string +} + +func ConvertCreateDisabledOdd(odd CreateDisabledOdd) dbgen.InsertDisabledOddsParams { + return dbgen.InsertDisabledOddsParams{ + OddID: odd.OddID, + EventID: odd.EventID, + RawOddID: odd.RawOddID, + } +} + +func ConvertDBDisabledOdd(dbDisabledOdd dbgen.DisabledOdd) DisabledOdd { + return DisabledOdd{ + ID: dbDisabledOdd.ID, + OddID: dbDisabledOdd.OddID, + RawOddID: dbDisabledOdd.RawOddID, + EventID: dbDisabledOdd.EventID, + CreatedAt: dbDisabledOdd.CreatedAt.Time, + } +} + +func ConvertDisabledOdds(list []dbgen.DisabledOdd) []DisabledOdd { + result := make([]DisabledOdd, 0, len(list)) + for _, item := range list { + result = append(result, ConvertDBDisabledOdd(item)) + } + return result +} diff --git a/internal/domain/oddres.go b/internal/domain/oddres.go index d02374a..2334b16 100644 --- a/internal/domain/oddres.go +++ b/internal/domain/oddres.go @@ -27,7 +27,7 @@ type RawOdd struct { // The Market ID for the json data can be either string / int which is causing problems when UnMarshalling type OddsMarket struct { - ID NullableInt64JSON `json:"id"` + ID ValidInt64 `json:"id"` Name string `json:"name"` Odds []json.RawMessage `json:"odds"` Header string `json:"header,omitempty"` diff --git a/internal/domain/odds_history.go b/internal/domain/odds_history.go index 58b8320..10da63a 100644 --- a/internal/domain/odds_history.go +++ b/internal/domain/odds_history.go @@ -54,3 +54,11 @@ func ConvertDBOddHistory(dbOddHistory dbgen.OddHistory) OddHistory { CreatedAt: dbOddHistory.CreatedAt.Time, } } + +func ConvertOddHistories(list []dbgen.OddHistory) []OddHistory { + result := make([]OddHistory, 0, len(list)) + for _, item := range list { + result = append(result, ConvertDBOddHistory(item)) + } + return result +} diff --git a/internal/domain/resultres.go b/internal/domain/resultres.go index 043c20b..381c86d 100644 --- a/internal/domain/resultres.go +++ b/internal/domain/resultres.go @@ -16,10 +16,10 @@ type LeagueRes struct { } type Team struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID NullableInt64JSON `json:"image_id"` - CC string `json:"cc"` + ID string `json:"id"` + Name string `json:"name"` + ImageID ValidInt64 `json:"image_id"` + CC string `json:"cc"` } type Score struct { @@ -28,14 +28,14 @@ type Score struct { } type CommonResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus NullableInt64JSON `json:"time_status"` - League LeagueRes `json:"league"` - Home Team `json:"home"` - Away Team `json:"away"` - SS string `json:"ss"` + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + TimeStatus ValidInt64 `json:"time_status"` + League LeagueRes `json:"league"` + Home Team `json:"home"` + Away Team `json:"away"` + SS string `json:"ss"` } type FootballResultResponse struct { diff --git a/internal/domain/validtypes.go b/internal/domain/validtypes.go new file mode 100644 index 0000000..709a401 --- /dev/null +++ b/internal/domain/validtypes.go @@ -0,0 +1,184 @@ +package domain + +import ( + "encoding/json" + "fmt" + "strconv" + "time" + + "github.com/jackc/pgx/v5/pgtype" +) + +type ValidInt64 struct { + Value int64 + Valid bool +} +type ValidInt struct { + Value int + Valid bool +} +type ValidInt32 struct { + Value int32 + Valid bool +} + +type ValidFloat32 struct { + Value float32 + Valid bool +} + +type ValidString struct { + Value string + Valid bool +} +type ValidTime struct { + Value time.Time + Valid bool +} +type ValidBool struct { + Value bool + Valid bool +} + +// ValidInt64 → pgtype.Int8 +func (v ValidInt64) ToPG() pgtype.Int8 { + return pgtype.Int8{ + Int64: v.Value, + Valid: v.Valid, + } +} + +// ValidInt32 → pgtype.Int4 +func (v ValidInt32) ToPG() pgtype.Int4 { + return pgtype.Int4{ + Int32: v.Value, + Valid: v.Valid, + } +} + +// ValidInt → pgtype.Int4 (Go int mapped to int32 for pg compatibility) +func (v ValidInt) ToPG() pgtype.Int4 { + return pgtype.Int4{ + Int32: int32(v.Value), + Valid: v.Valid, + } +} + +// ValidFloat32 → pgtype.Float4 +func (v ValidFloat32) ToPG() pgtype.Float4 { + return pgtype.Float4{ + Float32: v.Value, + Valid: v.Valid, + } +} + +// ValidString → pgtype.Text +func (v ValidString) ToPG() pgtype.Text { + return pgtype.Text{ + String: v.Value, + Valid: v.Valid, + } +} + +// ValidTime → pgtype.Timestamp +func (v ValidTime) ToPG() pgtype.Timestamp { + return pgtype.Timestamp{ + Time: v.Value, + Valid: v.Valid, + } +} + +// ValidBool → pgtype.Bool +func (v ValidBool) ToPG() pgtype.Bool { + return pgtype.Bool{ + Bool: v.Value, + Valid: v.Valid, + } +} + +func ConvertInt64Ptr(value *int64) ValidInt64 { + if value == nil { + return ValidInt64{} + } + return ValidInt64{ + Value: *value, + Valid: true, + } +} + +func ConvertInt32Ptr(value *int32) ValidInt32 { + if value == nil { + return ValidInt32{} + } + return ValidInt32{ + Value: *value, + Valid: true, + } +} + +func ConvertStringPtr(value *string) ValidString { + if value == nil { + return ValidString{} + } + return ValidString{ + Value: *value, + Valid: true, + } +} + +func ConvertFloat32Ptr(value *float32) ValidFloat32 { + if value == nil { + return ValidFloat32{} + } + return ValidFloat32{ + Value: *value, + Valid: true, + } +} + +func ConvertCurrencyFloatPtr(value *float32) ValidInt64 { + if value == nil { + return ValidInt64{} + } + + convertedCurrency := ToCurrency(*value) + + return ValidInt64{ + Value: int64(convertedCurrency), + Valid: true, + } +} + +func ConvertBoolPtr(value *bool) ValidBool { + if value == nil { + return ValidBool{} + } + return ValidBool{ + Value: *value, + Valid: true, + } +} + +func (n *ValidInt64) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err == nil { + if s == "" { + n.Valid = false + return nil + } + v, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return err + } + n.Value, n.Valid = v, true + return nil + } + + var v int64 + if err := json.Unmarshal(data, &v); err == nil { + n.Value, n.Valid = v, true + return nil + } + + return fmt.Errorf("invalid int64 value: %s", string(data)) +} \ No newline at end of file diff --git a/internal/repository/custom_odds.go b/internal/repository/custom_odds.go new file mode 100644 index 0000000..6b2506f --- /dev/null +++ b/internal/repository/custom_odds.go @@ -0,0 +1,5 @@ +package repository + +import "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + +func (s *Store) InsertCustomOdds(ctx context.Context, odd domain.ConvertCreateCustomOdd) (domain.) \ No newline at end of file diff --git a/internal/repository/disabled_odds.go b/internal/repository/disabled_odds.go new file mode 100644 index 0000000..b94ba65 --- /dev/null +++ b/internal/repository/disabled_odds.go @@ -0,0 +1,68 @@ +package repository + +import ( + "context" + "fmt" + + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +) + +func (s *Store) InsertDisabledOdd(ctx context.Context, odd domain.CreateDisabledOdd) (domain.DisabledOdd, error) { + dbDisabledOdd, err := s.queries.InsertDisabledOdds(ctx, domain.ConvertCreateDisabledOdd(odd)) + + if err != nil { + return domain.DisabledOdd{}, fmt.Errorf("InsertDisabledOdd failed: %w", err) + } + + return domain.ConvertDBDisabledOdd(dbDisabledOdd), nil +} + +func (s *Store) GetAllDisabledOdds(ctx context.Context) ([]domain.DisabledOdd, error) { + dbDisabledOdds, err := s.queries.GetAllDisabledOdds(ctx) + + if err != nil { + return nil, fmt.Errorf("GetAllDisabledOdds failed: %w", err) + } + + return domain.ConvertDisabledOdds(dbDisabledOdds), nil +} + +func (s *Store) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (domain.DisabledOdd, error) { + dbDisabledOdd, err := s.queries.GetDisabledOddByRawOddID(ctx, rawOddID) + + if err != nil { + return domain.DisabledOdd{}, fmt.Errorf("GetDisabledOddByRawOddID failed: %w", err) + } + + return domain.ConvertDBDisabledOdd(dbDisabledOdd), nil +} + +func (s *Store) GetDisabledOddByID(ctx context.Context, id int64) (domain.DisabledOdd, error) { + dbDisabledOdd, err := s.queries.GetDisabledOddByID(ctx, id) + + if err != nil { + return domain.DisabledOdd{}, fmt.Errorf("GetDisabledOddByID failed: %w", err) + } + + return domain.ConvertDBDisabledOdd(dbDisabledOdd), nil +} + +func (s *Store) DeleteDisabledOddsByID(ctx context.Context, id int64) error { + + err := s.queries.DeleteDisabledOddsByID(ctx, id) + if err != nil { + return fmt.Errorf("DeleteDisabledOddsByID failed: %w", err) + } + + return nil +} + +func (s *Store) DeleteDisabledOddsByRawOddID(ctx context.Context, id int64) error { + + err := s.queries.DeleteDisabledOddsByRawOddID(ctx, id) + if err != nil { + return fmt.Errorf("DeleteDisabledOddsByRawOddID failed: %w", err) + } + + return nil +} diff --git a/internal/repository/odd_history.go b/internal/repository/odd_history.go index b625155..115c572 100644 --- a/internal/repository/odd_history.go +++ b/internal/repository/odd_history.go @@ -18,14 +18,6 @@ func (s *Store) InsertOddHistory(ctx context.Context, odd domain.CreateOddHistor return domain.ConvertDBOddHistory(dbOddHistory), nil } -func convertOddHistories(list []dbgen.OddHistory) []domain.OddHistory { - result := make([]domain.OddHistory, 0, len(list)) - for _, item := range list { - result = append(result, domain.ConvertDBOddHistory(item)) - } - return result -} - func (s *Store) GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) { dbOddHistories, err := s.queries.GetAllOddHistory(ctx, dbgen.GetAllOddHistoryParams{ OddID: filter.OddID.ToPG(), @@ -40,7 +32,7 @@ func (s *Store) GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFi return nil, fmt.Errorf("GetAllOddHistory failed: %w", err) } - return convertOddHistories(dbOddHistories), nil + return domain.ConvertOddHistories(dbOddHistories), nil } func (s *Store) GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) { @@ -51,10 +43,11 @@ func (s *Store) GetInitialOddPerDay(ctx context.Context, filter domain.OddHistor EventID: filter.EventID.ToPG(), CreatedAfter: filter.CreatedAfter.ToPG(), CreatedBefore: filter.CreatedBefore.ToPG(), + DateTrunc: "day", }) if err != nil { return nil, fmt.Errorf("GetInitialOddPerDay failed: %w", err) } - return convertOddHistories(dbOddHistories), nil + return domain.ConvertOddHistories(dbOddHistories), nil } diff --git a/internal/services/santimpay/service.go b/internal/services/santimpay/service.go index 27e5b3e..18a15b6 100644 --- a/internal/services/santimpay/service.go +++ b/internal/services/santimpay/service.go @@ -237,7 +237,7 @@ func (s *SantimPayService) ProcessDirectPayment(ctx context.Context, req domain. if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } - + // 5. Save transfer in DB transfer := domain.CreateTransfer{ Amount: domain.Currency(req.Amount), diff --git a/internal/services/virtualGame/veli/service.go b/internal/services/virtualGame/veli/service.go index 4c675cc..5c4e1f4 100644 --- a/internal/services/virtualGame/veli/service.go +++ b/internal/services/virtualGame/veli/service.go @@ -259,7 +259,7 @@ func (s *service) ProcessWin(ctx context.Context, req domain.WinRequest) (*domai // --- 1. Validate PlayerID --- playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { - return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %s", req.PlayerID) +z`` return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %s", req.PlayerID) } // --- 2. Get player wallets ---