- Updated SQL queries to include number_of_outcomes in GetAllOdds, GetOddByID, GetOddsByEventID, and GetOddsByMarketID. - Modified data structures in domain and repository layers to accommodate number_of_outcomes. - Enhanced event models to track total odd outcomes. - Introduced new SQL scripts for development data seeding.
573 lines
19 KiB
Go
573 lines
19 KiB
Go
package domain
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
// TODO: turn status into an enum
|
|
// Status represents the status of an event.
|
|
// 0 Not Started
|
|
// 1 InPlay
|
|
// 2 TO BE FIXED
|
|
// 3 Ended
|
|
// 4 Postponed
|
|
// 5 Cancelled
|
|
// 6 Walkover
|
|
// 7 Interrupted
|
|
// 8 Abandoned
|
|
// 9 Retired
|
|
// 10 Suspended
|
|
// 11 Decided by FA
|
|
// 99 Removed
|
|
type EventStatus string
|
|
|
|
const (
|
|
STATUS_PENDING EventStatus = "upcoming"
|
|
STATUS_IN_PLAY EventStatus = "in_play"
|
|
STATUS_TO_BE_FIXED EventStatus = "to_be_fixed"
|
|
STATUS_ENDED EventStatus = "ended"
|
|
STATUS_POSTPONED EventStatus = "postponed"
|
|
STATUS_CANCELLED EventStatus = "cancelled"
|
|
STATUS_WALKOVER EventStatus = "walkover"
|
|
STATUS_INTERRUPTED EventStatus = "interrupted"
|
|
STATUS_ABANDONED EventStatus = "abandoned"
|
|
STATUS_RETIRED EventStatus = "retired"
|
|
STATUS_SUSPENDED EventStatus = "suspended"
|
|
STATUS_DECIDED_BY_FA EventStatus = "decided_by_fa"
|
|
STATUS_REMOVED EventStatus = "removed"
|
|
)
|
|
|
|
type EventSource string
|
|
|
|
const (
|
|
EVENT_SOURCE_BET365 EventSource = "b365api"
|
|
EVENT_SOURCE_BWIN EventSource = "bwin"
|
|
EVENT_SOURCE_BETFAIR EventSource = "bfair"
|
|
EVENT_SOURCE_1XBET EventSource = "1xbet"
|
|
EVENT_SOURCE_ENET EventSource = "enetpulse"
|
|
)
|
|
|
|
// --- EventStatus Validation ---
|
|
func (s EventStatus) IsValid() bool {
|
|
switch s {
|
|
case STATUS_PENDING,
|
|
STATUS_IN_PLAY,
|
|
STATUS_TO_BE_FIXED,
|
|
STATUS_ENDED,
|
|
STATUS_POSTPONED,
|
|
STATUS_CANCELLED,
|
|
STATUS_WALKOVER,
|
|
STATUS_INTERRUPTED,
|
|
STATUS_ABANDONED,
|
|
STATUS_RETIRED,
|
|
STATUS_SUSPENDED,
|
|
STATUS_DECIDED_BY_FA,
|
|
STATUS_REMOVED:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func ParseEventStatus(val string) (EventStatus, error) {
|
|
s := EventStatus(val)
|
|
if !s.IsValid() {
|
|
return "", fmt.Errorf("invalid EventStatus: %q", val)
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// --- EventSource Validation ---
|
|
func (s EventSource) IsValid() bool {
|
|
switch s {
|
|
case EVENT_SOURCE_BET365,
|
|
EVENT_SOURCE_BWIN,
|
|
EVENT_SOURCE_BETFAIR,
|
|
EVENT_SOURCE_1XBET,
|
|
EVENT_SOURCE_ENET:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func ParseEventSource(val string) (EventSource, error) {
|
|
s := EventSource(val)
|
|
if !s.IsValid() {
|
|
return "", fmt.Errorf("invalid EventSource: %q", val)
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
type BaseEvent struct {
|
|
ID int64
|
|
SourceEventID string
|
|
SportID int32
|
|
MatchName string
|
|
HomeTeam string
|
|
AwayTeam string
|
|
HomeTeamID int64
|
|
AwayTeamID int64
|
|
HomeTeamImage string
|
|
AwayTeamImage string
|
|
LeagueID int64
|
|
LeagueName string
|
|
LeagueCC ValidString
|
|
StartTime time.Time
|
|
Source EventSource
|
|
Status EventStatus
|
|
TotalOddOutcomes int64
|
|
IsMonitored bool
|
|
DefaultIsFeatured bool
|
|
DefaultIsActive bool
|
|
DefaultWinningUpperLimit int64
|
|
Score ValidString
|
|
MatchMinute ValidInt
|
|
TimerStatus ValidString
|
|
AddedTime ValidInt
|
|
MatchPeriod ValidInt
|
|
IsLive bool
|
|
FetchedAt time.Time
|
|
}
|
|
type BaseEventRes struct {
|
|
ID int64 `json:"id"`
|
|
SourceEventID string `json:"source_event_id"`
|
|
SportID int32 `json:"sport_id"`
|
|
MatchName string `json:"match_name"`
|
|
HomeTeam string `json:"home_team"`
|
|
AwayTeam string `json:"away_team"`
|
|
HomeTeamID int64 `json:"home_team_id"`
|
|
AwayTeamID int64 `json:"away_team_id"`
|
|
HomeTeamImage string `json:"home_team_image"`
|
|
AwayTeamImage string `json:"away_team_image"`
|
|
LeagueID int64 `json:"league_id"`
|
|
LeagueName string `json:"league_name"`
|
|
LeagueCC string `json:"league_cc"`
|
|
StartTime time.Time `json:"start_time"`
|
|
Source EventSource `json:"source"`
|
|
Status EventStatus `json:"status"`
|
|
TotalOddOutcomes int64 `json:"total_odd_outcomes"`
|
|
IsMonitored bool `json:"is_monitored"`
|
|
DefaultIsFeatured bool `json:"default_is_featured"`
|
|
DefaultIsActive bool `json:"default_is_active"`
|
|
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
|
|
Score string `json:"score"`
|
|
MatchMinute int `json:"match_minute"`
|
|
TimerStatus string `json:"timer_status"`
|
|
AddedTime int `json:"added_time"`
|
|
MatchPeriod int `json:"match_period"`
|
|
IsLive bool `json:"is_live"`
|
|
FetchedAt time.Time `json:"fetched_at"`
|
|
}
|
|
type EventWithSettings struct {
|
|
ID int64
|
|
SourceEventID string
|
|
SportID int32
|
|
MatchName string
|
|
HomeTeam string
|
|
AwayTeam string
|
|
HomeTeamID int64
|
|
AwayTeamID int64
|
|
HomeTeamImage string
|
|
AwayTeamImage string
|
|
LeagueID int64
|
|
LeagueName string
|
|
LeagueCC ValidString
|
|
StartTime time.Time
|
|
Source EventSource
|
|
Status EventStatus
|
|
TotalOddOutcomes int64
|
|
IsMonitored bool
|
|
IsFeatured bool
|
|
IsActive bool
|
|
WinningUpperLimit int64
|
|
DefaultIsFeatured bool
|
|
DefaultIsActive bool
|
|
DefaultWinningUpperLimit int64
|
|
Score ValidString
|
|
MatchMinute ValidInt
|
|
TimerStatus ValidString
|
|
AddedTime ValidInt
|
|
MatchPeriod ValidInt
|
|
IsLive bool
|
|
UpdatedAt time.Time
|
|
FetchedAt time.Time
|
|
}
|
|
|
|
type CreateEvent struct {
|
|
SourceEventID string
|
|
SportID int32
|
|
MatchName string
|
|
HomeTeam string
|
|
AwayTeam string
|
|
HomeTeamID int64
|
|
AwayTeamID int64
|
|
HomeTeamImage string
|
|
AwayTeamImage string
|
|
LeagueID int64
|
|
LeagueName string
|
|
StartTime time.Time
|
|
IsLive bool
|
|
Status EventStatus
|
|
Source EventSource
|
|
DefaultWinningUpperLimit int64
|
|
}
|
|
|
|
type EventWithSettingsRes struct {
|
|
ID int64 `json:"id"`
|
|
SourceEventID string `json:"source_event_id"`
|
|
SportID int32 `json:"sport_id"`
|
|
MatchName string `json:"match_name"`
|
|
HomeTeam string `json:"home_team"`
|
|
AwayTeam string `json:"away_team"`
|
|
HomeTeamID int64 `json:"home_team_id"`
|
|
AwayTeamID int64 `json:"away_team_id"`
|
|
HomeTeamImage string `json:"home_team_image"`
|
|
AwayTeamImage string `json:"away_team_image"`
|
|
LeagueID int64 `json:"league_id"`
|
|
LeagueName string `json:"league_name"`
|
|
LeagueCC string `json:"league_cc"`
|
|
StartTime time.Time `json:"start_time"`
|
|
Source EventSource `json:"source"`
|
|
Status EventStatus `json:"status"`
|
|
TotalOddOutcomes int64 `json:"total_odd_outcomes"`
|
|
IsMonitored bool `json:"is_monitored"`
|
|
IsFeatured bool `json:"is_featured"`
|
|
IsActive bool `json:"is_active"`
|
|
WinningUpperLimit int64 `json:"winning_upper_limit"`
|
|
DefaultIsFeatured bool `json:"default_is_featured"`
|
|
DefaultIsActive bool `json:"default_is_active"`
|
|
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
|
|
Score string `json:"score,omitempty"`
|
|
MatchMinute int `json:"match_minute,omitempty"`
|
|
TimerStatus string `json:"timer_status,omitempty"`
|
|
AddedTime int `json:"added_time,omitempty"`
|
|
MatchPeriod int `json:"match_period,omitempty"`
|
|
IsLive bool `json:"is_live"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
FetchedAt time.Time `json:"fetched_at"`
|
|
}
|
|
|
|
type EventSettings struct {
|
|
CompanyID int64
|
|
EventID int64
|
|
IsActive ValidBool
|
|
IsFeatured ValidBool
|
|
WinningUpperLimit ValidInt
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
type UpdateTenantEventSettings struct {
|
|
CompanyID int64
|
|
EventID int64
|
|
IsActive ValidBool
|
|
IsFeatured ValidBool
|
|
WinningUpperLimit ValidInt64
|
|
}
|
|
type UpdateGlobalEventSettings struct {
|
|
EventID int64
|
|
IsActive ValidBool
|
|
IsFeatured ValidBool
|
|
WinningUpperLimit ValidInt64
|
|
}
|
|
|
|
type ValidEventStatus struct {
|
|
Value EventStatus
|
|
Valid bool
|
|
}
|
|
|
|
func (v ValidEventStatus) ToPG() pgtype.Text {
|
|
return pgtype.Text{
|
|
String: string(v.Value),
|
|
Valid: v.Valid,
|
|
}
|
|
}
|
|
|
|
type ValidEventSource struct {
|
|
Value EventSource
|
|
Valid bool
|
|
}
|
|
|
|
func (v ValidEventSource) ToPG() pgtype.Text {
|
|
return pgtype.Text{
|
|
String: string(v.Value),
|
|
Valid: v.Valid,
|
|
}
|
|
}
|
|
|
|
type EventFilter struct {
|
|
Query ValidString
|
|
SportID ValidInt32
|
|
LeagueID ValidInt64
|
|
CountryCode ValidString
|
|
FirstStartTime ValidTime
|
|
LastStartTime ValidTime
|
|
Limit ValidInt32
|
|
Offset ValidInt32
|
|
Featured ValidBool
|
|
Active ValidBool
|
|
IsLive ValidBool
|
|
Status ValidEventStatus
|
|
Source ValidEventSource
|
|
}
|
|
|
|
func ConvertDBEvent(event dbgen.EventWithCountry) BaseEvent {
|
|
return BaseEvent{
|
|
ID: event.ID,
|
|
SourceEventID: event.SourceEventID,
|
|
SportID: event.SportID,
|
|
MatchName: event.MatchName,
|
|
HomeTeam: event.HomeTeam,
|
|
AwayTeam: event.AwayTeam,
|
|
HomeTeamID: event.HomeTeamID,
|
|
AwayTeamID: event.AwayTeamID,
|
|
HomeTeamImage: event.HomeKitImage,
|
|
AwayTeamImage: event.AwayKitImage,
|
|
LeagueID: event.LeagueID,
|
|
LeagueName: event.LeagueName,
|
|
LeagueCC: ValidString{
|
|
Value: event.LeagueCc.String,
|
|
Valid: event.LeagueCc.Valid,
|
|
},
|
|
StartTime: event.StartTime.Time.UTC(),
|
|
Source: EventSource(event.Source),
|
|
Status: EventStatus(event.Status),
|
|
TotalOddOutcomes: event.TotalOutcomes,
|
|
DefaultIsFeatured: event.DefaultIsFeatured,
|
|
IsMonitored: event.IsMonitored,
|
|
DefaultIsActive: event.DefaultIsActive,
|
|
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
|
|
Score: ValidString{
|
|
Value: event.Score.String,
|
|
Valid: event.Score.Valid,
|
|
},
|
|
MatchMinute: ValidInt{
|
|
Value: int(event.MatchMinute.Int32),
|
|
Valid: event.MatchMinute.Valid,
|
|
},
|
|
TimerStatus: ValidString{
|
|
Value: event.TimerStatus.String,
|
|
Valid: event.TimerStatus.Valid,
|
|
},
|
|
AddedTime: ValidInt{
|
|
Value: int(event.AddedTime.Int32),
|
|
Valid: event.AddedTime.Valid,
|
|
},
|
|
MatchPeriod: ValidInt{
|
|
Value: int(event.MatchPeriod.Int32),
|
|
Valid: event.MatchPeriod.Valid,
|
|
},
|
|
IsLive: event.IsLive,
|
|
FetchedAt: event.FetchedAt.Time,
|
|
}
|
|
}
|
|
|
|
func ConvertDBEvents(events []dbgen.EventWithCountry) []BaseEvent {
|
|
result := make([]BaseEvent, len(events))
|
|
for i, e := range events {
|
|
result[i] = ConvertDBEvent(e)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
|
|
return dbgen.InsertEventParams{
|
|
SourceEventID: e.SourceEventID,
|
|
SportID: e.SportID,
|
|
MatchName: e.MatchName,
|
|
HomeTeam: e.HomeTeam,
|
|
AwayTeam: e.AwayTeam,
|
|
HomeTeamID: e.HomeTeamID,
|
|
AwayTeamID: e.AwayTeamID,
|
|
HomeKitImage: e.HomeTeamImage,
|
|
AwayKitImage: e.AwayTeamImage,
|
|
LeagueID: e.LeagueID,
|
|
LeagueName: e.LeagueName,
|
|
StartTime: pgtype.Timestamp{Time: e.StartTime, Valid: true},
|
|
IsLive: e.IsLive,
|
|
Status: string(e.Status),
|
|
Source: string(e.Source),
|
|
DefaultWinningUpperLimit: e.DefaultWinningUpperLimit,
|
|
}
|
|
}
|
|
|
|
func ConvertCreateEventSettings(eventSettings UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams {
|
|
return dbgen.SaveTenantEventSettingsParams{
|
|
CompanyID: eventSettings.CompanyID,
|
|
EventID: eventSettings.EventID,
|
|
IsActive: eventSettings.IsActive.ToPG(),
|
|
IsFeatured: eventSettings.IsFeatured.ToPG(),
|
|
WinningUpperLimit: eventSettings.WinningUpperLimit.ToPG(),
|
|
}
|
|
}
|
|
|
|
func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings {
|
|
return EventWithSettings{
|
|
ID: event.ID,
|
|
SourceEventID: event.SourceEventID,
|
|
WinningUpperLimit: event.WinningUpperLimit,
|
|
SportID: event.SportID,
|
|
MatchName: event.MatchName,
|
|
HomeTeam: event.HomeTeam,
|
|
AwayTeam: event.AwayTeam,
|
|
HomeTeamID: event.HomeTeamID,
|
|
AwayTeamID: event.AwayTeamID,
|
|
HomeTeamImage: event.HomeKitImage,
|
|
AwayTeamImage: event.AwayKitImage,
|
|
LeagueID: event.LeagueID,
|
|
LeagueName: event.LeagueName,
|
|
LeagueCC: ValidString{
|
|
Value: event.LeagueCc.String,
|
|
Valid: event.LeagueCc.Valid,
|
|
},
|
|
StartTime: event.StartTime.Time.UTC(),
|
|
Source: EventSource(event.Source),
|
|
Status: EventStatus(event.Status),
|
|
TotalOddOutcomes: event.TotalOutcomes,
|
|
IsFeatured: event.IsFeatured,
|
|
IsMonitored: event.IsMonitored,
|
|
IsActive: event.IsActive,
|
|
DefaultIsFeatured: event.DefaultIsFeatured,
|
|
DefaultIsActive: event.DefaultIsActive,
|
|
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
|
|
Score: ValidString{
|
|
Value: event.Score.String,
|
|
Valid: event.Score.Valid,
|
|
},
|
|
MatchMinute: ValidInt{
|
|
Value: int(event.MatchMinute.Int32),
|
|
Valid: event.MatchMinute.Valid,
|
|
},
|
|
TimerStatus: ValidString{
|
|
Value: event.TimerStatus.String,
|
|
Valid: event.TimerStatus.Valid,
|
|
},
|
|
AddedTime: ValidInt{
|
|
Value: int(event.AddedTime.Int32),
|
|
Valid: event.AddedTime.Valid,
|
|
},
|
|
MatchPeriod: ValidInt{
|
|
Value: int(event.MatchPeriod.Int32),
|
|
Valid: event.MatchPeriod.Valid,
|
|
},
|
|
IsLive: event.IsLive,
|
|
UpdatedAt: event.UpdatedAt.Time,
|
|
FetchedAt: event.FetchedAt.Time,
|
|
}
|
|
}
|
|
|
|
func ConvertDBEventWithSettings(events []dbgen.EventWithSetting) []EventWithSettings {
|
|
result := make([]EventWithSettings, len(events))
|
|
for i, e := range events {
|
|
result[i] = ConvertDBEventWithSetting(e)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func ConvertUpdateTenantEventSettings(event UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams {
|
|
return dbgen.SaveTenantEventSettingsParams{
|
|
EventID: event.EventID,
|
|
CompanyID: event.CompanyID,
|
|
IsActive: event.IsActive.ToPG(),
|
|
IsFeatured: event.IsFeatured.ToPG(),
|
|
WinningUpperLimit: event.WinningUpperLimit.ToPG(),
|
|
}
|
|
}
|
|
func ConvertUpdateGlobalEventSettings(event UpdateGlobalEventSettings) dbgen.UpdateGlobalEventSettingsParams {
|
|
return dbgen.UpdateGlobalEventSettingsParams{
|
|
ID: event.EventID,
|
|
DefaultIsActive: event.IsActive.ToPG(),
|
|
DefaultIsFeatured: event.IsFeatured.ToPG(),
|
|
DefaultWinningUpperLimit: event.WinningUpperLimit.ToPG(),
|
|
}
|
|
}
|
|
|
|
func ConvertEventRes(event BaseEvent) BaseEventRes {
|
|
return BaseEventRes{
|
|
ID: event.ID,
|
|
SourceEventID: event.SourceEventID,
|
|
SportID: event.SportID,
|
|
MatchName: event.MatchName,
|
|
HomeTeam: event.HomeTeam,
|
|
AwayTeam: event.AwayTeam,
|
|
HomeTeamID: event.HomeTeamID,
|
|
AwayTeamID: event.AwayTeamID,
|
|
HomeTeamImage: event.HomeTeamImage,
|
|
AwayTeamImage: event.AwayTeamImage,
|
|
LeagueID: event.LeagueID,
|
|
LeagueName: event.LeagueName,
|
|
LeagueCC: event.LeagueCC.Value,
|
|
StartTime: event.StartTime.UTC(),
|
|
Source: EventSource(event.Source),
|
|
Status: EventStatus(event.Status),
|
|
TotalOddOutcomes: event.TotalOddOutcomes,
|
|
DefaultIsFeatured: event.DefaultIsFeatured,
|
|
IsMonitored: event.IsMonitored,
|
|
DefaultIsActive: event.DefaultIsActive,
|
|
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
|
|
Score: event.Score.Value,
|
|
MatchMinute: event.MatchMinute.Value,
|
|
TimerStatus: event.TimerStatus.Value,
|
|
AddedTime: event.AddedTime.Value,
|
|
MatchPeriod: event.MatchPeriod.Value,
|
|
IsLive: event.IsLive,
|
|
FetchedAt: event.FetchedAt.UTC(),
|
|
}
|
|
}
|
|
func ConvertEventResList(events []BaseEvent) []BaseEventRes {
|
|
result := make([]BaseEventRes, 0, len(events))
|
|
for _, event := range events {
|
|
result = append(result, ConvertEventRes(event))
|
|
}
|
|
return result
|
|
}
|
|
|
|
func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
|
|
return EventWithSettingsRes{
|
|
ID: event.ID,
|
|
SourceEventID: event.SourceEventID,
|
|
SportID: event.SportID,
|
|
MatchName: event.MatchName,
|
|
HomeTeam: event.HomeTeam,
|
|
AwayTeam: event.AwayTeam,
|
|
HomeTeamID: event.HomeTeamID,
|
|
AwayTeamID: event.AwayTeamID,
|
|
HomeTeamImage: event.HomeTeamImage,
|
|
AwayTeamImage: event.AwayTeamImage,
|
|
LeagueID: event.LeagueID,
|
|
LeagueName: event.LeagueName,
|
|
LeagueCC: event.LeagueCC.Value,
|
|
StartTime: event.StartTime.UTC(),
|
|
Source: EventSource(event.Source),
|
|
Status: EventStatus(event.Status),
|
|
TotalOddOutcomes: event.TotalOddOutcomes,
|
|
IsFeatured: event.IsFeatured,
|
|
IsMonitored: event.IsMonitored,
|
|
IsActive: event.IsActive,
|
|
DefaultIsFeatured: event.DefaultIsFeatured,
|
|
DefaultIsActive: event.DefaultIsActive,
|
|
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
|
|
WinningUpperLimit: event.WinningUpperLimit,
|
|
Score: event.Score.Value,
|
|
MatchMinute: event.MatchMinute.Value,
|
|
TimerStatus: event.TimerStatus.Value,
|
|
AddedTime: event.AddedTime.Value,
|
|
MatchPeriod: event.MatchPeriod.Value,
|
|
IsLive: event.IsLive,
|
|
FetchedAt: event.FetchedAt.UTC(),
|
|
UpdatedAt: event.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
func ConvertEventWithSettingResList(events []EventWithSettings) []EventWithSettingsRes {
|
|
result := make([]EventWithSettingsRes, 0, len(events))
|
|
for _, event := range events {
|
|
result = append(result, ConvertEventWitSettingRes(event))
|
|
}
|
|
return result
|
|
}
|