Refactor event and league domain models to include default settings, enhance odds handling, improve API routes, fixed get global settings and Incremented API version to 1.0.dev13.

This commit is contained in:
Samuel Tariku 2025-09-02 04:24:37 +03:00
parent 0b03bfaa42
commit 6ecc6f0428
20 changed files with 688 additions and 277 deletions

View File

@ -54,7 +54,7 @@ SET sport_id = EXCLUDED.sport_id,
is_live = EXCLUDED.is_live,
source = EXCLUDED.source,
fetched_at = now();
-- name: InsertEventSettings :exec
-- name: SaveEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
@ -243,19 +243,7 @@ WHERE id = $1;
UPDATE events
SET is_monitored = $1
WHERE id = $2;
-- name: UpdateEventSettings :exec
UPDATE company_event_settings
SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured = COALESCE(
sqlc.narg('is_featured'),
is_featured
),
winning_upper_limit = COALESCE(
sqlc.narg('winning_upper_limit'),
winning_upper_limit
)
WHERE event_id = $1
AND company_id = $2;
-- name: DeleteEvent :exec
DELETE FROM events
WHERE id = $1;

View File

@ -36,6 +36,10 @@ WHERE (
sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL
)
AND (
name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
ORDER BY name ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetAllLeaguesWithSettings :many
@ -58,6 +62,11 @@ WHERE (company_id = $1)
is_featured = sqlc.narg('is_featured')
OR sqlc.narg('is_featured') IS NULL
)
AND (
name ILIKE '%' || sqlc.narg('query') || '%'
OR league_name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
ORDER BY is_featured DESC,
name ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');

View File

@ -26,7 +26,7 @@ SET market_type = EXCLUDED.market_type,
raw_odds = EXCLUDED.raw_odds,
fetched_at = EXCLUDED.fetched_at,
expires_at = EXCLUDED.expires_at;
-- name: InsertOddSettings :exec
-- name: SaveOddSettings :exec
INSERT INTO company_odd_settings (
company_id,
odds_market_id,
@ -46,6 +46,10 @@ SELECT *
FROM odds_market_with_settings
WHERE company_id = $1
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetOddByID :one
SELECT *
FROM odds_market_with_event
WHERE id = $1;
-- name: GetOddsByMarketID :one
SELECT *
FROM odds_market_with_event
@ -57,6 +61,11 @@ FROM odds_market_with_settings
WHERE market_id = $1
AND event_id = $2
AND company_id = $3;
-- name: GetOddsWithSettingsByID :one
SELECT *
FROM odds_market_with_settings
WHERE id = $1
AND company_id = $2;
-- name: GetOddsByEventID :many
SELECT *
FROM odds_market_with_event

View File

@ -654,40 +654,6 @@ func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error
return err
}
const InsertEventSettings = `-- name: InsertEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
is_active,
is_featured,
winning_upper_limit
)
VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured,
winning_upper_limit = EXCLUDED.winning_upper_limit
`
type InsertEventSettingsParams struct {
CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
}
func (q *Queries) InsertEventSettings(ctx context.Context, arg InsertEventSettingsParams) error {
_, err := q.db.Exec(ctx, InsertEventSettings,
arg.CompanyID,
arg.EventID,
arg.IsActive,
arg.IsFeatured,
arg.WinningUpperLimit,
)
return err
}
const IsEventMonitored = `-- name: IsEventMonitored :one
SELECT is_monitored
FROM events
@ -727,6 +693,40 @@ func (q *Queries) ListLiveEvents(ctx context.Context) ([]string, error) {
return items, nil
}
const SaveEventSettings = `-- name: SaveEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
is_active,
is_featured,
winning_upper_limit
)
VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured,
winning_upper_limit = EXCLUDED.winning_upper_limit
`
type SaveEventSettingsParams struct {
CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
}
func (q *Queries) SaveEventSettings(ctx context.Context, arg SaveEventSettingsParams) error {
_, err := q.db.Exec(ctx, SaveEventSettings,
arg.CompanyID,
arg.EventID,
arg.IsActive,
arg.IsFeatured,
arg.WinningUpperLimit,
)
return err
}
const UpdateEventMonitored = `-- name: UpdateEventMonitored :exec
UPDATE events
SET is_monitored = $1
@ -743,40 +743,6 @@ func (q *Queries) UpdateEventMonitored(ctx context.Context, arg UpdateEventMonit
return err
}
const UpdateEventSettings = `-- name: UpdateEventSettings :exec
UPDATE company_event_settings
SET is_active = COALESCE($3, is_active),
is_featured = COALESCE(
$4,
is_featured
),
winning_upper_limit = COALESCE(
$5,
winning_upper_limit
)
WHERE event_id = $1
AND company_id = $2
`
type UpdateEventSettingsParams struct {
EventID string `json:"event_id"`
CompanyID int64 `json:"company_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
}
func (q *Queries) UpdateEventSettings(ctx context.Context, arg UpdateEventSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateEventSettings,
arg.EventID,
arg.CompanyID,
arg.IsActive,
arg.IsFeatured,
arg.WinningUpperLimit,
)
return err
}
const UpdateMatchResult = `-- name: UpdateMatchResult :exec
UPDATE events
SET score = $1,

View File

@ -44,13 +44,18 @@ WHERE (
sport_id = $2
OR $2 IS NULL
)
AND (
name ILIKE '%' || $3 || '%'
OR $3 IS NULL
)
ORDER BY name ASC
LIMIT $4 OFFSET $3
LIMIT $5 OFFSET $4
`
type GetAllLeaguesParams struct {
CountryCode pgtype.Text `json:"country_code"`
SportID pgtype.Int4 `json:"sport_id"`
Query pgtype.Text `json:"query"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
@ -59,6 +64,7 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
rows, err := q.db.Query(ctx, GetAllLeagues,
arg.CountryCode,
arg.SportID,
arg.Query,
arg.Offset,
arg.Limit,
)
@ -109,9 +115,14 @@ WHERE (company_id = $1)
is_featured = $5
OR $5 IS NULL
)
AND (
name ILIKE '%' || $6 || '%'
OR league_name ILIKE '%' || $6 || '%'
OR $6 IS NULL
)
ORDER BY is_featured DESC,
name ASC
LIMIT $7 OFFSET $6
LIMIT $8 OFFSET $7
`
type GetAllLeaguesWithSettingsParams struct {
@ -120,6 +131,7 @@ type GetAllLeaguesWithSettingsParams struct {
SportID pgtype.Int4 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
Query pgtype.Text `json:"query"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
@ -131,6 +143,7 @@ func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeagu
arg.SportID,
arg.IsActive,
arg.IsFeatured,
arg.Query,
arg.Offset,
arg.Limit,
)

View File

@ -114,6 +114,34 @@ func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWith
return items, nil
}
const GetOddByID = `-- name: GetOddByID :one
SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event
WHERE id = $1
`
func (q *Queries) GetOddByID(ctx context.Context, id int64) (OddsMarketWithEvent, error) {
row := q.db.QueryRow(ctx, GetOddByID, id)
var i OddsMarketWithEvent
err := row.Scan(
&i.ID,
&i.EventID,
&i.MarketType,
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
&i.IsMonitored,
&i.IsLive,
&i.Status,
&i.Source,
)
return i, err
}
const GetOddsByEventID = `-- name: GetOddsByEventID :many
SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event
@ -272,6 +300,39 @@ func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsW
return items, nil
}
const GetOddsWithSettingsByID = `-- name: GetOddsWithSettingsByID :one
SELECT id, event_id, market_type, market_name, market_category, market_id, default_is_active, fetched_at, expires_at, company_id, is_active, raw_odds, updated_at
FROM odds_market_with_settings
WHERE id = $1
AND company_id = $2
`
type GetOddsWithSettingsByIDParams struct {
ID int64 `json:"id"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSettingsByIDParams) (OddsMarketWithSetting, error) {
row := q.db.QueryRow(ctx, GetOddsWithSettingsByID, arg.ID, arg.CompanyID)
var i OddsMarketWithSetting
err := row.Scan(
&i.ID,
&i.EventID,
&i.MarketType,
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
&i.CompanyID,
&i.IsActive,
&i.RawOdds,
&i.UpdatedAt,
)
return i, err
}
const GetOddsWithSettingsByMarketID = `-- name: GetOddsWithSettingsByMarketID :one
SELECT id, event_id, market_type, market_name, market_category, market_id, default_is_active, fetched_at, expires_at, company_id, is_active, raw_odds, updated_at
FROM odds_market_with_settings
@ -307,36 +368,6 @@ func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOdds
return i, err
}
const InsertOddSettings = `-- name: InsertOddSettings :exec
INSERT INTO company_odd_settings (
company_id,
odds_market_id,
is_active,
custom_raw_odds
)
VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
custom_raw_odds = EXCLUDED.custom_raw_odds
`
type InsertOddSettingsParams struct {
CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
IsActive pgtype.Bool `json:"is_active"`
CustomRawOdds []byte `json:"custom_raw_odds"`
}
func (q *Queries) InsertOddSettings(ctx context.Context, arg InsertOddSettingsParams) error {
_, err := q.db.Exec(ctx, InsertOddSettings,
arg.CompanyID,
arg.OddsMarketID,
arg.IsActive,
arg.CustomRawOdds,
)
return err
}
const InsertOddsMarket = `-- name: InsertOddsMarket :exec
INSERT INTO odds_market (
event_id,
@ -391,3 +422,33 @@ func (q *Queries) InsertOddsMarket(ctx context.Context, arg InsertOddsMarketPara
)
return err
}
const SaveOddSettings = `-- name: SaveOddSettings :exec
INSERT INTO company_odd_settings (
company_id,
odds_market_id,
is_active,
custom_raw_odds
)
VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
custom_raw_odds = EXCLUDED.custom_raw_odds
`
type SaveOddSettingsParams struct {
CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
IsActive pgtype.Bool `json:"is_active"`
CustomRawOdds []byte `json:"custom_raw_odds"`
}
func (q *Queries) SaveOddSettings(ctx context.Context, arg SaveOddSettingsParams) error {
_, err := q.db.Exec(ctx, SaveOddSettings,
arg.CompanyID,
arg.OddsMarketID,
arg.IsActive,
arg.CustomRawOdds,
)
return err
}

View File

@ -98,11 +98,11 @@ type BaseEventRes struct {
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"`
DefaultWinningUpperLimit int32 `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"`
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"`
}
@ -122,10 +122,13 @@ type EventWithSettings struct {
StartTime time.Time
Source EventSource
Status EventStatus
IsFeatured bool
IsMonitored bool
IsFeatured bool
IsActive bool
WinningUpperLimit int32
DefaultIsFeatured bool
DefaultIsActive bool
DefaultWinningUpperLimit int32
Score ValidString
MatchMinute ValidInt
TimerStatus ValidString
@ -170,10 +173,13 @@ type EventWithSettingsRes struct {
StartTime time.Time `json:"start_time"`
Source EventSource `json:"source"`
Status EventStatus `json:"status"`
IsFeatured bool `json:"is_featured"`
IsMonitored bool `json:"is_monitored"`
IsFeatured bool `json:"is_featured"`
IsActive bool `json:"is_active"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
Score string `json:"score,omitempty"`
MatchMinute int `json:"match_minute,omitempty"`
TimerStatus string `json:"timer_status,omitempty"`
@ -291,8 +297,8 @@ func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
}
}
func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.InsertEventSettingsParams {
return dbgen.InsertEventSettingsParams{
func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.SaveEventSettingsParams {
return dbgen.SaveEventSettingsParams{
CompanyID: eventSettings.CompanyID,
EventID: eventSettings.EventID,
IsActive: eventSettings.IsActive.ToPG(),
@ -324,7 +330,9 @@ func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings {
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
WinningUpperLimit: event.WinningUpperLimit,
DefaultIsFeatured: event.DefaultIsFeatured,
DefaultIsActive: event.DefaultIsActive,
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
Score: ValidString{
Value: event.Score.String,
Valid: event.Score.Valid,
@ -359,8 +367,8 @@ func ConvertDBEventWithSettings(events []dbgen.EventWithSetting) []EventWithSett
return result
}
func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.UpdateEventSettingsParams {
return dbgen.UpdateEventSettingsParams{
func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.SaveEventSettingsParams {
return dbgen.SaveEventSettingsParams{
EventID: event.EventID,
CompanyID: event.CompanyID,
IsActive: event.IsActive.ToPG(),
@ -427,6 +435,9 @@ func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
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,

View File

@ -16,16 +16,22 @@ type LeagueWithSettings struct {
SportID int32
IsActive bool
IsFeatured bool
DefaultIsActive bool
DefaultIsFeatured bool
UpdatedAt time.Time
}
type LeagueWithSettingsRes struct {
ID int64 `json:"id" example:"1"`
Name string `json:"name" example:"BPL"`
CompanyID int64 `json:"company_id" example:"1"`
CountryCode string `json:"cc" example:"uk"`
Bet365ID int32 `json:"bet365_id" example:"1121"`
IsActive bool `json:"is_active" example:"false"`
SportID int32 `json:"sport_id" example:"1"`
IsFeatured bool `json:"is_featured" example:"false"`
DefaultIsActive bool `json:"default_is_active" example:"false"`
DefaultIsFeatured bool `json:"default_is_featured" example:"false"`
UpdatedAt time.Time `json:"updated_at"`
}
type BaseLeague struct {
ID int64
@ -82,6 +88,7 @@ type UpdateLeague struct {
}
type LeagueFilter struct {
Query ValidString
CountryCode ValidString
SportID ValidInt32
IsActive ValidBool
@ -154,6 +161,9 @@ func ConvertDBLeagueWithSetting(lws dbgen.LeagueWithSetting) LeagueWithSettings
SportID: lws.SportID,
IsFeatured: lws.IsFeatured,
UpdatedAt: lws.UpdatedAt.Time,
DefaultIsActive: lws.DefaultIsActive,
DefaultIsFeatured: lws.DefaultIsFeatured,
}
}
@ -174,3 +184,50 @@ func ConvertUpdateLeague(updateLeague UpdateLeague) dbgen.UpdateLeagueParams {
SportID: updateLeague.SportID.ToPG(),
}
}
func ConvertLeagueWithSettingRes(lws LeagueWithSettings) LeagueWithSettingsRes {
return LeagueWithSettingsRes{
ID: lws.ID,
Name: lws.Name,
CompanyID: lws.CompanyID,
CountryCode: lws.CountryCode.Value,
Bet365ID: lws.Bet365ID.Value,
IsActive: lws.IsActive,
SportID: lws.SportID,
IsFeatured: lws.IsFeatured,
UpdatedAt: lws.UpdatedAt,
DefaultIsActive: lws.DefaultIsActive,
DefaultIsFeatured: lws.DefaultIsFeatured,
}
}
func ConvertLeagueWithSettingResList(leagues []LeagueWithSettings) []LeagueWithSettingsRes {
result := make([]LeagueWithSettingsRes, len(leagues))
for i, lws := range leagues {
result[i] = ConvertLeagueWithSettingRes(lws)
}
return result
}
func ConvertBaseLeagueRes(league BaseLeague) BaseLeagueRes {
return BaseLeagueRes{
ID: league.ID,
Name: league.Name,
CountryCode: league.CountryCode.Value,
Bet365ID: league.Bet365ID.Value,
SportID: league.SportID,
DefaultIsActive: league.DefaultIsActive,
DefaultIsFeatured: league.DefaultIsFeatured,
}
}
func ConvertBaseLeagueResList(leagues []BaseLeague) []BaseLeagueRes {
result := make([]BaseLeagueRes, len(leagues))
for i, league := range leagues {
result[i] = ConvertBaseLeagueRes(league)
}
return result
}

View File

@ -61,6 +61,17 @@ type CreateOddMarketSettings struct {
CustomRawOdds []map[string]interface{}
}
type CustomOdd struct {
OddID int64 `json:"odd_id"`
OddValue float32 `json:"odd_value"`
}
type CreateOddMarketSettingsReq struct {
OddMarketID int64 `json:"odd_market_id"`
IsActive *bool `json:"is_active,omitempty"`
CustomOdd []CustomOdd `json:"custom_odd,omitempty"`
}
// type RawOddsByMarketID struct {
// ID int64 `json:"id"`
// MarketName string `json:"market_name"`
@ -136,12 +147,12 @@ func ConvertCreateOddMarket(oddMarket CreateOddMarket) (dbgen.InsertOddsMarketPa
}, nil
}
func ConvertCreateOddMarketSetting(oms CreateOddMarketSettings) (dbgen.InsertOddSettingsParams, error) {
func ConvertCreateOddMarketSetting(oms CreateOddMarketSettings) (dbgen.SaveOddSettingsParams, error) {
rawOddsBytes, err := json.Marshal(oms.CustomRawOdds)
if err != nil {
return dbgen.InsertOddSettingsParams{}, err
return dbgen.SaveOddSettingsParams{}, err
}
return dbgen.InsertOddSettingsParams{
return dbgen.SaveOddSettingsParams{
CompanyID: oms.CompanyID,
OddsMarketID: oms.OddMarketID,
IsActive: oms.IsActive.ToPG(),

View File

@ -35,6 +35,18 @@ type SettingListRes struct {
CashbackAmountCap float32 `json:"cashback_amount_cap"`
}
func ConvertSettingListRes(settings SettingList) SettingListRes {
return SettingListRes{
SMSProvider: settings.SMSProvider,
MaxNumberOfOutcomes: settings.MaxNumberOfOutcomes,
BetAmountLimit: settings.BetAmountLimit.Float32(),
DailyTicketPerIP: settings.DailyTicketPerIP,
TotalWinningLimit: settings.TotalWinningLimit.Float32(),
AmountForBetReferral: settings.AmountForBetReferral.Float32(),
CashbackAmountCap: settings.CashbackAmountCap.Float32(),
}
}
type SaveSettingListReq struct {
SMSProvider *string `json:"sms_provider,omitempty"`
MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"`
@ -157,57 +169,105 @@ func (vsl *ValidSettingList) GetAllValid() map[string]*bool {
return settingValid
}
func setValidSetting[T any](settings map[string]*T, searchKey string, setVal T) error {
for key, setting := range settings {
// func setValidSetting[T any](settings map[string]*T, searchKey string, searchVal string, setVal func(string) (T, error)) error {
// for key, setting := range settings {
// if key == searchKey {
// s, err := setVal(searchVal)
// if err != nil {
// return err
// }
// *setting = s
// }
// return nil
// }
// return ErrSettingNotFound
// }
func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetInt64SettingsMap() {
if key == searchKey {
*setting = setVal
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
*setting = ValidInt64{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error {
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
return setValidSetting(vsl.GetInt64SettingsMap(), searchKey, ValidInt64{Value: value, Valid: true})
}
func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetCurrencySettingsMap() {
if key == searchKey {
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
return setValidSetting(vsl.GetCurrencySettingsMap(), searchKey, ValidCurrency{Value: Currency(value), Valid: true})
*setting = ValidCurrency{Value: Currency(value), Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error {
return setValidSetting(vsl.GetStringSettingsMap(), searchKey, ValidString{Value: searchVal, Valid: true})
for key, setting := range vsl.GetStringSettingsMap() {
if key == searchKey {
*setting = ValidString{Value: searchVal, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetBoolSettingsMap() {
if key == searchKey {
value, err := strconv.ParseBool(searchVal)
if err != nil {
return err
}
return setValidSetting(vsl.GetBoolSettingsMap(), searchKey, ValidBool{Value: value, Valid: true})
*setting = ValidBool{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetFloat32SettingsMap() {
if key == searchKey {
value, err := strconv.ParseFloat(searchVal, 32)
if err != nil {
return err
}
return setValidSetting(vsl.GetFloat32SettingsMap(), searchKey, ValidFloat32{Value: float32(value), Valid: true})
*setting = ValidFloat32{Value: float32(value), Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetTimeSettingsMap() {
if key == searchKey {
value, err := time.Parse(time.RFC3339, searchVal)
if err != nil {
return err
}
return setValidSetting(vsl.GetTimeSettingsMap(), searchKey, ValidTime{Value: value, Valid: true})
*setting = ValidTime{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error {
@ -223,9 +283,10 @@ func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) erro
for _, setter := range setters {
if err := setter(searchKey, searchVal); err != nil {
if err == ErrSettingNotFound {
// fmt.Printf("setting is not found %v \n", searchKey)
continue // not this setter, try the next
}
return fmt.Errorf("error while processing setting %q: %w", searchKey, err)
return fmt.Errorf("error while processing setting %q: %w \n", searchKey, err)
}
return nil // successfully set
}
@ -306,6 +367,7 @@ func validateSettings[T any](
var errs []string
for key, s := range settings {
if !customValidator(s) {
errs = append(errs, fmt.Sprintf("%v is invalid", key))
}
}
@ -378,6 +440,7 @@ func (vsl *ValidSettingList) ValidateAllSettings() error {
for _, validator := range validators {
if err := validator(); err != nil {
errs = append(errs, err.Error())
}
}
@ -410,12 +473,12 @@ func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, er
if err == ErrSettingNotFound {
MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
}
MongoDBLogger.Error("unknown error while fetching settings", zap.Error(err))
}
}
if err := dbSettingList.ValidateAllSettings(); err != nil {
fmt.Printf("setting validation error: %v \n", err)
MongoDBLogger.Warn("setting validation error", zap.Error(err))
MongoDBLogger.Warn("setting validation error", zap.Error(err), zap.Any("db_setting_list", dbSettingList))
return SettingList{}, err
}
@ -436,7 +499,6 @@ func ConvertDBOverrideSettingList(settings []dbgen.GetOverrideSettingsRow) (Sett
}
if err := dbSettingList.ValidateAllSettings(); err != nil {
fmt.Printf("setting validation error: %v \n", err)
MongoDBLogger.Warn("setting validation error", zap.Error(err))
return SettingList{}, err
}

View File

@ -16,9 +16,6 @@ func (s *Store) SaveEvent(ctx context.Context, e domain.CreateEvent) error {
return s.queries.InsertEvent(ctx, domain.ConvertCreateEvent(e))
}
func (s *Store) InsertEventSettings(ctx context.Context, eventSetting domain.CreateEventSettings) error {
return s.queries.InsertEventSettings(ctx, domain.ConvertCreateEventSettings(eventSetting))
}
func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) {
return s.queries.ListLiveEvents(ctx)
@ -176,7 +173,7 @@ func (s *Store) UpdateEventMonitored(ctx context.Context, eventID string, IsMoni
}
func (s *Store) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error {
return s.queries.UpdateEventSettings(ctx, domain.ConvertUpdateEventSettings(event))
return s.queries.SaveEventSettings(ctx, domain.ConvertUpdateEventSettings(event));
}
func (s *Store) DeleteEvent(ctx context.Context, eventID string) error {

View File

@ -18,6 +18,7 @@ func (s *Store) SaveLeagueSettings(ctx context.Context, leagueSettings domain.Cr
func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error) {
l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{
Query: filter.Query.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
SportID: filter.SportID.ToPG(),
Limit: pgtype.Int4{
@ -38,6 +39,7 @@ func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) (
func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error) {
l, err := s.queries.GetAllLeaguesWithSettings(ctx, dbgen.GetAllLeaguesWithSettingsParams{
Query: filter.Query.ToPG(),
CompanyID: companyID,
CountryCode: filter.CountryCode.ToPG(),
SportID: filter.SportID.ToPG(),

View File

@ -97,6 +97,21 @@ func (s *Store) GetAllOddsWithSettings(ctx context.Context, companyID int64, fil
return domainOdds, nil
}
func (s *Store) GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error) {
odd, err := s.queries.GetOddByID(ctx, id)
if err != nil {
return domain.OddMarket{}, err
}
convertedOdd, err := domain.ConvertDBOddMarket(odd)
if err != nil {
return domain.OddMarket{}, err
}
return convertedOdd, nil
}
func (s *Store) GetOddsByMarketID(ctx context.Context, marketID string, eventID string) (domain.OddMarket, error) {
odds, err := s.queries.GetOddsByMarketID(ctx, dbgen.GetOddsByMarketIDParams{
@ -134,6 +149,25 @@ func (s *Store) GetOddsWithSettingsByMarketID(ctx context.Context, marketID stri
return convertedOdd, nil
}
func (s *Store) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error) {
odds, err := s.queries.GetOddsWithSettingsByID(ctx, dbgen.GetOddsWithSettingsByIDParams{
ID: ID,
CompanyID: companyID,
})
if err != nil {
return domain.OddMarketWithSettings{}, err
}
convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds)
if err != nil {
return domain.OddMarketWithSettings{}, err
}
return convertedOdd, nil
}
func (s *Store) GetOddsByEventID(ctx context.Context, upcomingID string, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) {
odds, err := s.queries.GetOddsByEventID(ctx, dbgen.GetOddsByEventIDParams{
EventID: upcomingID,
@ -185,3 +219,13 @@ func (s *Store) GetOddsWithSettingsByEventID(ctx context.Context, upcomingID str
func (s *Store) DeleteOddsForEvent(ctx context.Context, eventID string) error {
return s.queries.DeleteOddsForEvent(ctx, eventID)
}
func (s *Store) SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error {
res, err := domain.ConvertCreateOddMarketSetting(odd)
if err != nil {
return nil
}
return s.queries.SaveOddSettings(ctx, res)
}

View File

@ -18,6 +18,11 @@ type Service interface {
// GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.OddMarket, error)
DeleteOddsForEvent(ctx context.Context, eventID string) error
GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error)
GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error)
// Settings
SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error
// Odd History
InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error)
GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error)

View File

@ -705,6 +705,46 @@ func (s *ServiceImpl) GetAllOddsWithSettings(ctx context.Context, companyID int6
return s.store.GetAllOddsWithSettings(ctx, companyID, filter)
}
func (s *ServiceImpl) GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error) {
return s.store.GetOddByID(ctx, id)
}
func (s *ServiceImpl) SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error {
return s.store.SaveOddsSetting(ctx, odd)
}
func (s *ServiceImpl) SaveOddsSettingReq(ctx context.Context, companyID int64, req domain.CreateOddMarketSettingsReq) error {
odd, err := s.GetOddsWithSettingsByID(ctx, req.OddMarketID, companyID)
if err != nil {
return err
}
newOdds, err := convertRawMessage(odd.RawOdds)
if err != nil {
return err
}
if len(req.CustomOdd) != 0 {
for _, customOdd := range req.CustomOdd {
for _, newOdd := range newOdds {
oldRawOddID := getInt(newOdd["id"])
if oldRawOddID == int(customOdd.OddID) {
newOdd["odds"] = customOdd.OddValue
}
}
}
}
return s.SaveOddsSetting(ctx, domain.CreateOddMarketSettings{
CompanyID: companyID,
OddMarketID: req.OddMarketID,
IsActive: domain.ConvertBoolPtr(req.IsActive),
CustomRawOdds: newOdds,
})
}
func (s *ServiceImpl) GetOddsByMarketID(ctx context.Context, marketID string, eventID string) (domain.OddMarket, error) {
rows, err := s.store.GetOddsByMarketID(ctx, marketID, eventID)
if err != nil {
@ -722,6 +762,10 @@ func (s *ServiceImpl) GetOddsWithSettingsByMarketID(ctx context.Context, marketI
return rows, nil
}
func (s *ServiceImpl) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error) {
return s.store.GetOddsWithSettingsByID(ctx, ID, companyID)
}
func (s *ServiceImpl) GetOddsByEventID(ctx context.Context, upcomingID string, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) {
return s.store.GetOddsByEventID(ctx, upcomingID, filter)
}

View File

@ -511,7 +511,7 @@ type UpdateEventSettingsReq struct {
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/events/{id}/settings [put]
// @Router /api/v1/tenant/{tenant_slug}/events/{id}/settings [put]
func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
@ -523,7 +523,7 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
var req UpdateEventSettingsReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("Failed to parse user id",
h.BadRequestLogger().Info("Failed to parse event id",
zap.String("eventID", eventID),
zap.Error(err),
)
@ -543,7 +543,7 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Error("Failed to update event featured",
h.BadRequestLogger().Error("Failed to update event settings",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
@ -557,7 +557,63 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update event featured", append(logFields, zap.Error(err))...)
h.InternalServerErrorLogger().Error("Failed to update event settings", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil)
}
type SetEventIsMonitoredReq struct {
IsMonitored bool `json:"is_monitored"`
}
// SetEventIsMonitored godoc
// @Summary update the event is_monitored
// @Description Update the event is_monitored
// @Tags event
// @Accept json
// @Produce json
// @Param id path int true "Event ID"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/events/{id}/is_monitored [patch]
func (h *Handler) SetEventIsMonitored(c *fiber.Ctx) error {
eventID := c.Params("id")
var req SetEventIsMonitoredReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("Failed to parse bet id",
zap.String("eventID", eventID),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
logFields := []zap.Field{
zap.String("eventID", eventID),
zap.Any("is_featured", req.IsMonitored),
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Error("Failed to update event featured",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err := h.eventSvc.UpdateEventMonitored(c.Context(), eventID, req.IsMonitored)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update event is_monitored", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}

View File

@ -33,6 +33,12 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
Valid: true,
}
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
countryCodeQuery := c.Query("cc")
countryCode := domain.ValidString{
Value: countryCodeQuery,
@ -73,6 +79,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
zap.Bool("is_active_valid", isActive.Valid),
zap.String("cc_query", countryCodeQuery),
zap.String("cc", countryCode.Value),
zap.String("query", searchQuery),
zap.Bool("cc_valid", countryCode.Valid),
zap.String("sport_id_query", sportIDQuery),
zap.Int32("sport_id", sportID.Value),
@ -85,6 +92,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
SportID: sportID,
Limit: limit,
Offset: offset,
Query: searchString,
})
if err != nil {
@ -93,7 +101,10 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", leagues, nil)
res := domain.ConvertBaseLeagueResList(leagues)
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", res, nil)
}
// GetAllLeaguesForTenant godoc
@ -125,6 +136,11 @@ func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
Valid: true,
}
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
countryCodeQuery := c.Query("cc")
countryCode := domain.ValidString{
Value: countryCodeQuery,
@ -179,6 +195,7 @@ func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
SportID: sportID,
Limit: limit,
Offset: offset,
Query: searchString,
})
if err != nil {
@ -186,7 +203,10 @@ func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
h.InternalServerErrorLogger().Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", leagues, nil)
res := domain.ConvertLeagueWithSettingResList(leagues)
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", res, nil)
}
type SetLeagueActiveReq struct {

View File

@ -1,6 +1,7 @@
package handlers
import (
"fmt"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -300,3 +301,49 @@ func (h *Handler) GetTenantOddsByUpcomingID(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Odds retrieved successfully", odds, nil)
}
func (h *Handler) SaveOddsSetting(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req domain.CreateOddMarketSettingsReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("Failed to parse event id",
zap.Int64("CompanyID", companyID.Value),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
logFields := []zap.Field{
zap.Int64("companyID", companyID.Value),
zap.Any("is_active", req.IsActive),
zap.Any("custom_odd", req.CustomOdd),
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Error("Failed to insert odd settings",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err := h.prematchSvc.SaveOddsSettingReq(c.Context(), companyID.Value, req)
if err != nil {
logFields = append(logFields, zap.Error(err))
h.InternalServerErrorLogger().Error("Failed to save odds settings", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to save odds settings"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Odds Settings saved successfully", nil, nil)
}

View File

@ -17,12 +17,15 @@ func (h *Handler) GetGlobalSettingList(c *fiber.Ctx) error {
h.mongoLoggerSvc.Error("Failed to fetch settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", settingsList, nil)
res := domain.ConvertSettingListRes(settingsList)
return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", res, nil)
}
func (h *Handler) GetGlobalSettingByKey(c *fiber.Ctx) error {
@ -100,7 +103,9 @@ func (h *Handler) UpdateGlobalSettingList(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "setting updated", settingsList, nil)
res := domain.ConvertSettingListRes(settingsList)
return response.WriteJSON(c, fiber.StatusOK, "setting updated", res, nil)
}
func (h *Handler) SaveCompanySettingList(c *fiber.Ctx) error {
@ -178,7 +183,9 @@ func (h *Handler) GetCompanySettingList(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", settingsList, nil)
res := domain.ConvertSettingListRes(settingsList)
return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", res, nil)
}
// /api/v1/{tenant_slug}/settings/{key}

View File

@ -58,7 +58,7 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "Welcome to the FortuneBet API",
"version": "1.0dev12",
"version": "1.0.dev13",
})
})
@ -236,10 +236,12 @@ func (a *App) initAppRoutes() {
tenant.Get("/odds", h.GetAllTenantOdds)
tenant.Get("/odds/upcoming/:upcoming_id", h.GetTenantOddsByUpcomingID)
tenant.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetTenantOddsByMarketID)
tenant.Post("/odds/settings", a.CompanyOnly, h.SaveOddsSetting)
groupV1.Get("/events", a.authMiddleware, h.GetAllUpcomingEvents)
groupV1.Get("/events/:id", a.authMiddleware, h.GetUpcomingEventByID)
groupV1.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved)
groupV1.Patch("/events/:id/is_monitored", a.authMiddleware, a.SuperAdminOnly, h.SetEventIsMonitored)
tenant.Get("/events", h.GetTenantUpcomingEvents)
tenant.Get("/events/:id", h.GetTenantEventByID)