fix: resolved issue on event, odds and creating a bet

This commit is contained in:
Samuel Tariku 2025-09-03 03:16:37 +03:00
parent 1c7c7e5731
commit 6b09c3c8d2
38 changed files with 952 additions and 328 deletions

View File

@ -112,13 +112,11 @@ func main() {
authSvc := authentication.NewService(store, store, cfg.RefreshExpiry)
userSvc := user.NewService(store, store, messengerSvc, cfg)
eventSvc := event.New(cfg.Bet365Token, store, domain.MongoDBLogger)
eventSvc := event.New(cfg.Bet365Token, store, *settingSvc, domain.MongoDBLogger)
oddsSvc := odds.New(store, cfg, eventSvc, logger, domain.MongoDBLogger)
notificationRepo := repository.NewNotificationRepository(store)
virtuaGamesRepo := repository.NewVirtualGameRepository(store)
notificationSvc := notificationservice.New(notificationRepo, domain.MongoDBLogger, logger, cfg, messengerSvc, userSvc)
var notificatioStore notificationservice.NotificationStore
// var userStore user.UserStore
// Initialize producer
@ -130,7 +128,6 @@ func main() {
wallet.WalletStore(store),
wallet.TransferStore(store),
wallet.DirectDepositStore(store),
notificatioStore,
notificationSvc,
userSvc,
domain.MongoDBLogger,
@ -153,7 +150,7 @@ func main() {
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
veliCLient := veli.NewClient(cfg, walletSvc)
veliVirtualGameService := veli.New(virtualGameSvc,vitualGameRepo, veliCLient, walletSvc, wallet.TransferStore(store), cfg)
veliVirtualGameService := veli.New(virtualGameSvc, vitualGameRepo, veliCLient, walletSvc, wallet.TransferStore(store), cfg)
recommendationSvc := recommendation.NewService(recommendationRepo)
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)

View File

@ -1,5 +1,4 @@
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Locations Initial Data
INSERT INTO branch_locations (key, value)
VALUES ('addis_ababa', 'Addis Ababa'),
@ -75,7 +74,6 @@ VALUES ('addis_ababa', 'Addis Ababa'),
('agaro', 'Agaro') ON CONFLICT (key) DO
UPDATE
SET value = EXCLUDED.value;
-- Settings Initial Data
INSERT INTO global_settings (key, value)
VALUES ('sms_provider', 'afro_message'),
@ -84,8 +82,8 @@ VALUES ('sms_provider', 'afro_message'),
('daily_ticket_limit', '50'),
('total_winnings_limit', '1000000'),
('amount_for_bet_referral', '1000000'),
('cashback_amount_cap', '1000') ON CONFLICT (key) DO NOTHING;
('cashback_amount_cap', '1000'),
('default_winning_limit', '5000000') ON CONFLICT (key) DO NOTHING;
-- Users
INSERT INTO users (
id,
@ -224,7 +222,7 @@ VALUES (
),
(
3,
20000,
100000000 ,
TRUE,
TRUE,
TRUE,
@ -237,7 +235,7 @@ VALUES (
),
(
4,
15000,
50000000,
TRUE,
TRUE,
TRUE,

View File

@ -33,7 +33,7 @@ VALUES (
6,
'Kirubel',
'Kibru',
'modernkibru @gmail.com',
'modernkibru@gmail.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'customer',
@ -98,7 +98,7 @@ VALUES (
),
(
7,
10000,
1000000,
TRUE,
TRUE,
TRUE,

View File

@ -36,7 +36,6 @@ CREATE TABLE IF NOT EXISTS virtual_game_providers (
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ
);
CREATE TABLE IF NOT EXISTS virtual_games (
id BIGSERIAL PRIMARY KEY,
game_id VARCHAR(150) NOT NULL,
@ -45,19 +44,16 @@ CREATE TABLE IF NOT EXISTS virtual_games (
category VARCHAR(100),
device_type VARCHAR(100),
volatility VARCHAR(50),
rtp NUMERIC(5,2),
rtp NUMERIC(5, 2),
has_demo BOOLEAN DEFAULT FALSE,
has_free_bets BOOLEAN DEFAULT FALSE,
bets NUMERIC[] DEFAULT '{}',
bets NUMERIC [] DEFAULT '{}',
thumbnail TEXT,
status INT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ
);
CREATE UNIQUE INDEX IF NOT EXISTS ux_virtual_games_provider_game
ON virtual_games (provider_id, game_id);
CREATE UNIQUE INDEX IF NOT EXISTS ux_virtual_games_provider_game ON virtual_games (provider_id, game_id);
CREATE TABLE IF NOT EXISTS wallets (
id BIGSERIAL PRIMARY KEY,
balance BIGINT NOT NULL DEFAULT 0,
@ -325,7 +321,7 @@ CREATE TABLE events (
),
default_is_active BOOLEAN NOT NULL DEFAULT true,
default_is_featured BOOLEAN NOT NULL DEFAULT false,
default_winning_upper_limit INT NOT NULL,
default_winning_upper_limit BIGINT NOT NULL,
is_monitored BOOLEAN NOT NULL DEFAULT FALSE,
UNIQUE (id, source)
);
@ -621,7 +617,7 @@ SELECT l.*,
COALESCE(cls.is_featured, l.default_is_featured) AS is_featured,
cls.updated_at
FROM leagues l
JOIN company_league_settings cls ON l.id = cls.league_id;
LEFT JOIN company_league_settings cls ON l.id = cls.league_id;
CREATE VIEW event_with_settings AS
SELECT e.*,
ces.company_id,
@ -634,7 +630,7 @@ SELECT e.*,
ces.updated_at,
l.country_code as league_cc
FROM events e
JOIN company_event_settings ces ON e.id = ces.event_id
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
JOIN leagues l ON l.id = e.league_id;
CREATE VIEW event_with_country AS
SELECT events.*,
@ -656,7 +652,7 @@ SELECT o.id,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
JOIN company_odd_settings cos ON o.id = cos.odds_market_id;
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id;
CREATE VIEW odds_market_with_event AS
SELECT o.*,
e.is_monitored,

View File

@ -14,7 +14,8 @@ INSERT INTO events (
start_time,
is_live,
status,
source
source,
default_winning_upper_limit
)
VALUES (
$1,
@ -31,7 +32,8 @@ VALUES (
$12,
$13,
$14,
$15
$15,
$16
) ON CONFLICT (id) DO
UPDATE
SET sport_id = EXCLUDED.sport_id,
@ -44,7 +46,6 @@ SET sport_id = EXCLUDED.sport_id,
away_kit_image = EXCLUDED.away_kit_image,
league_id = EXCLUDED.league_id,
league_name = EXCLUDED.league_name,
league_cc = EXCLUDED.league_cc,
start_time = EXCLUDED.start_time,
score = EXCLUDED.score,
match_minute = EXCLUDED.match_minute,
@ -53,6 +54,7 @@ SET sport_id = EXCLUDED.sport_id,
match_period = EXCLUDED.match_period,
is_live = EXCLUDED.is_live,
source = EXCLUDED.source,
default_winning_upper_limit = EXCLUDED.default_winning_upper_limit,
fetched_at = now();
-- name: SaveEventSettings :exec
INSERT INTO company_event_settings (
@ -152,16 +154,18 @@ ORDER BY start_time ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetTotalCompanyEvents :one
SELECT COUNT(*)
FROM event_with_settings
WHERE company_id = $1
AND is_live = false
FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $1
JOIN leagues l ON l.id = e.league_id
WHERE is_live = false
AND status = 'upcoming'
AND (
league_id = sqlc.narg('league_id')
OR sqlc.narg('league_id') IS NULL
)
AND (
sport_id = sqlc.narg('sport_id')
e.sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL
)
AND (
@ -178,14 +182,29 @@ WHERE company_id = $1
OR sqlc.narg('first_start_time') IS NULL
)
AND (
league_cc = sqlc.narg('country_code')
l.country_code = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL
)
AND (
ces.is_featured = sqlc.narg('is_featured')
OR sqlc.narg('is_featured') IS NULL
);
-- name: GetEventsWithSettings :many
SELECT *
FROM event_with_settings
WHERE company_id = $1
AND start_time > now()
SELECT e.*,
ces.company_id,
COALESCE(ces.is_active, e.default_is_active) AS is_active,
COALESCE(ces.is_featured, e.default_is_featured) AS is_featured,
COALESCE(
ces.winning_upper_limit,
e.default_winning_upper_limit
) AS winning_upper_limit,
ces.updated_at,
l.country_code as league_cc
FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $1
JOIN leagues l ON l.id = e.league_id
WHERE start_time > now()
AND is_live = false
AND status = 'upcoming'
AND (
@ -193,7 +212,7 @@ WHERE company_id = $1
OR sqlc.narg('league_id') IS NULL
)
AND (
sport_id = sqlc.narg('sport_id')
e.sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL
)
AND (
@ -210,9 +229,12 @@ WHERE company_id = $1
OR sqlc.narg('first_start_time') IS NULL
)
AND (
league_cc = sqlc.narg('country_code')
l.country_code = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL
)
) AND (
ces.is_featured = sqlc.narg('is_featured')
OR sqlc.narg('is_featured') IS NULL
)
ORDER BY start_time ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetUpcomingByID :one
@ -223,10 +245,21 @@ WHERE id = $1
AND status = 'upcoming'
LIMIT 1;
-- name: GetEventWithSettingByID :one
SELECT *
FROM event_with_settings
WHERE id = $1
AND company_id = $2
SELECT e.*,
ces.company_id,
COALESCE(ces.is_active, e.default_is_active) AS is_active,
COALESCE(ces.is_featured, e.default_is_featured) AS is_featured,
COALESCE(
ces.winning_upper_limit,
e.default_winning_upper_limit
) AS winning_upper_limit,
ces.updated_at,
l.country_code as league_cc
FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $2
JOIN leagues l ON l.id = e.league_id
WHERE e.id = $1
AND is_live = false
AND status = 'upcoming'
LIMIT 1;
@ -243,7 +276,6 @@ WHERE id = $1;
UPDATE events
SET is_monitored = $1
WHERE id = $2;
-- name: DeleteEvent :exec
DELETE FROM events
WHERE id = $1;

View File

@ -43,10 +43,14 @@ WHERE (
ORDER BY name ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetAllLeaguesWithSettings :many
SELECT *
FROM league_with_settings
WHERE (company_id = $1)
AND (
SELECT l.*,
cls.company_id,
COALESCE(cls.is_active, l.default_is_active) AS is_active,
COALESCE(cls.is_featured, l.default_is_featured) AS is_featured,
cls.updated_at
FROM leagues l
LEFT JOIN company_league_settings cls ON l.id = cls.league_id AND company_id = $1
WHERE (
country_code = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL
)
@ -64,7 +68,6 @@ WHERE (company_id = $1)
)
AND (
name ILIKE '%' || sqlc.narg('query') || '%'
OR league_name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
ORDER BY is_featured DESC,

View File

@ -42,9 +42,22 @@ SELECT *
FROM odds_market_with_event
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetAllOddsWithSettings :many
SELECT *
FROM odds_market_with_settings
WHERE company_id = $1
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id
AND company_id = $1
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetOddByID :one
SELECT *
@ -56,16 +69,42 @@ FROM odds_market_with_event
WHERE market_id = $1
AND event_id = $2;
-- name: GetOddsWithSettingsByMarketID :one
SELECT *
FROM odds_market_with_settings
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id
AND company_id = $3
WHERE market_id = $1
AND event_id = $2
AND company_id = $3;
AND event_id = $2;
-- name: GetOddsWithSettingsByID :one
SELECT *
FROM odds_market_with_settings
WHERE id = $1
AND company_id = $2;
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id
AND company_id = $2
WHERE o.id = $1;
-- name: GetOddsByEventID :many
SELECT *
FROM odds_market_with_event
@ -84,10 +123,23 @@ WHERE event_id = $1
)
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetOddsWithSettingsByEventID :many
SELECT *
FROM odds_market_with_settings
WHERE event_id = $1
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id
AND company_id = $2
WHERE event_id = $1
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: DeleteOddsForEvent :exec
DELETE FROM odds_market

View File

@ -78,10 +78,21 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]EventWithCountry,
}
const GetEventWithSettingByID = `-- name: GetEventWithSettingByID :one
SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, company_id, is_active, is_featured, winning_upper_limit, updated_at, league_cc
FROM event_with_settings
WHERE id = $1
AND company_id = $2
SELECT e.id, e.sport_id, e.match_name, e.home_team, e.away_team, e.home_team_id, e.away_team_id, e.home_kit_image, e.away_kit_image, e.league_id, e.league_name, e.start_time, e.score, e.match_minute, e.timer_status, e.added_time, e.match_period, e.is_live, e.status, e.fetched_at, e.source, e.default_is_active, e.default_is_featured, e.default_winning_upper_limit, e.is_monitored,
ces.company_id,
COALESCE(ces.is_active, e.default_is_active) AS is_active,
COALESCE(ces.is_featured, e.default_is_featured) AS is_featured,
COALESCE(
ces.winning_upper_limit,
e.default_winning_upper_limit
) AS winning_upper_limit,
ces.updated_at,
l.country_code as league_cc
FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $2
JOIN leagues l ON l.id = e.league_id
WHERE e.id = $1
AND is_live = false
AND status = 'upcoming'
LIMIT 1
@ -92,9 +103,43 @@ type GetEventWithSettingByIDParams struct {
CompanyID int64 `json:"company_id"`
}
func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithSettingByIDParams) (EventWithSetting, error) {
type GetEventWithSettingByIDRow struct {
ID string `json:"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"`
HomeKitImage string `json:"home_kit_image"`
AwayKitImage string `json:"away_kit_image"`
LeagueID int64 `json:"league_id"`
LeagueName string `json:"league_name"`
StartTime pgtype.Timestamp `json:"start_time"`
Score pgtype.Text `json:"score"`
MatchMinute pgtype.Int4 `json:"match_minute"`
TimerStatus pgtype.Text `json:"timer_status"`
AddedTime pgtype.Int4 `json:"added_time"`
MatchPeriod pgtype.Int4 `json:"match_period"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
LeagueCc pgtype.Text `json:"league_cc"`
}
func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithSettingByIDParams) (GetEventWithSettingByIDRow, error) {
row := q.db.QueryRow(ctx, GetEventWithSettingByID, arg.ID, arg.CompanyID)
var i EventWithSetting
var i GetEventWithSettingByIDRow
err := row.Scan(
&i.ID,
&i.SportID,
@ -132,10 +177,21 @@ func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithS
}
const GetEventsWithSettings = `-- name: GetEventsWithSettings :many
SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, company_id, is_active, is_featured, winning_upper_limit, updated_at, league_cc
FROM event_with_settings
WHERE company_id = $1
AND start_time > now()
SELECT e.id, e.sport_id, e.match_name, e.home_team, e.away_team, e.home_team_id, e.away_team_id, e.home_kit_image, e.away_kit_image, e.league_id, e.league_name, e.start_time, e.score, e.match_minute, e.timer_status, e.added_time, e.match_period, e.is_live, e.status, e.fetched_at, e.source, e.default_is_active, e.default_is_featured, e.default_winning_upper_limit, e.is_monitored,
ces.company_id,
COALESCE(ces.is_active, e.default_is_active) AS is_active,
COALESCE(ces.is_featured, e.default_is_featured) AS is_featured,
COALESCE(
ces.winning_upper_limit,
e.default_winning_upper_limit
) AS winning_upper_limit,
ces.updated_at,
l.country_code as league_cc
FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $1
JOIN leagues l ON l.id = e.league_id
WHERE start_time > now()
AND is_live = false
AND status = 'upcoming'
AND (
@ -143,7 +199,7 @@ WHERE company_id = $1
OR $2 IS NULL
)
AND (
sport_id = $3
e.sport_id = $3
OR $3 IS NULL
)
AND (
@ -160,11 +216,14 @@ WHERE company_id = $1
OR $6 IS NULL
)
AND (
league_cc = $7
l.country_code = $7
OR $7 IS NULL
)
) AND (
ces.is_featured = $8
OR $8 IS NULL
)
ORDER BY start_time ASC
LIMIT $9 OFFSET $8
LIMIT $10 OFFSET $9
`
type GetEventsWithSettingsParams struct {
@ -175,11 +234,46 @@ type GetEventsWithSettingsParams struct {
LastStartTime pgtype.Timestamp `json:"last_start_time"`
FirstStartTime pgtype.Timestamp `json:"first_start_time"`
CountryCode pgtype.Text `json:"country_code"`
IsFeatured pgtype.Bool `json:"is_featured"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSettingsParams) ([]EventWithSetting, error) {
type GetEventsWithSettingsRow struct {
ID string `json:"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"`
HomeKitImage string `json:"home_kit_image"`
AwayKitImage string `json:"away_kit_image"`
LeagueID int64 `json:"league_id"`
LeagueName string `json:"league_name"`
StartTime pgtype.Timestamp `json:"start_time"`
Score pgtype.Text `json:"score"`
MatchMinute pgtype.Int4 `json:"match_minute"`
TimerStatus pgtype.Text `json:"timer_status"`
AddedTime pgtype.Int4 `json:"added_time"`
MatchPeriod pgtype.Int4 `json:"match_period"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
LeagueCc pgtype.Text `json:"league_cc"`
}
func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSettingsParams) ([]GetEventsWithSettingsRow, error) {
rows, err := q.db.Query(ctx, GetEventsWithSettings,
arg.CompanyID,
arg.LeagueID,
@ -188,6 +282,7 @@ func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSe
arg.LastStartTime,
arg.FirstStartTime,
arg.CountryCode,
arg.IsFeatured,
arg.Offset,
arg.Limit,
)
@ -195,9 +290,9 @@ func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSe
return nil, err
}
defer rows.Close()
var items []EventWithSetting
var items []GetEventsWithSettingsRow
for rows.Next() {
var i EventWithSetting
var i GetEventsWithSettingsRow
if err := rows.Scan(
&i.ID,
&i.SportID,
@ -403,16 +498,18 @@ func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginat
const GetTotalCompanyEvents = `-- name: GetTotalCompanyEvents :one
SELECT COUNT(*)
FROM event_with_settings
WHERE company_id = $1
AND is_live = false
FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $1
JOIN leagues l ON l.id = e.league_id
WHERE is_live = false
AND status = 'upcoming'
AND (
league_id = $2
OR $2 IS NULL
)
AND (
sport_id = $3
e.sport_id = $3
OR $3 IS NULL
)
AND (
@ -429,9 +526,13 @@ WHERE company_id = $1
OR $6 IS NULL
)
AND (
league_cc = $7
l.country_code = $7
OR $7 IS NULL
)
AND (
ces.is_featured = $8
OR $8 IS NULL
)
`
type GetTotalCompanyEventsParams struct {
@ -442,6 +543,7 @@ type GetTotalCompanyEventsParams struct {
LastStartTime pgtype.Timestamp `json:"last_start_time"`
FirstStartTime pgtype.Timestamp `json:"first_start_time"`
CountryCode pgtype.Text `json:"country_code"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) GetTotalCompanyEvents(ctx context.Context, arg GetTotalCompanyEventsParams) (int64, error) {
@ -453,6 +555,7 @@ func (q *Queries) GetTotalCompanyEvents(ctx context.Context, arg GetTotalCompany
arg.LastStartTime,
arg.FirstStartTime,
arg.CountryCode,
arg.IsFeatured,
)
var count int64
err := row.Scan(&count)
@ -573,7 +676,8 @@ INSERT INTO events (
start_time,
is_live,
status,
source
source,
default_winning_upper_limit
)
VALUES (
$1,
@ -590,7 +694,8 @@ VALUES (
$12,
$13,
$14,
$15
$15,
$16
) ON CONFLICT (id) DO
UPDATE
SET sport_id = EXCLUDED.sport_id,
@ -603,7 +708,6 @@ SET sport_id = EXCLUDED.sport_id,
away_kit_image = EXCLUDED.away_kit_image,
league_id = EXCLUDED.league_id,
league_name = EXCLUDED.league_name,
league_cc = EXCLUDED.league_cc,
start_time = EXCLUDED.start_time,
score = EXCLUDED.score,
match_minute = EXCLUDED.match_minute,
@ -612,25 +716,27 @@ SET sport_id = EXCLUDED.sport_id,
match_period = EXCLUDED.match_period,
is_live = EXCLUDED.is_live,
source = EXCLUDED.source,
default_winning_upper_limit = EXCLUDED.default_winning_upper_limit,
fetched_at = now()
`
type InsertEventParams struct {
ID string `json:"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"`
HomeKitImage string `json:"home_kit_image"`
AwayKitImage string `json:"away_kit_image"`
LeagueID int64 `json:"league_id"`
LeagueName string `json:"league_name"`
StartTime pgtype.Timestamp `json:"start_time"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
Source string `json:"source"`
ID string `json:"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"`
HomeKitImage string `json:"home_kit_image"`
AwayKitImage string `json:"away_kit_image"`
LeagueID int64 `json:"league_id"`
LeagueName string `json:"league_name"`
StartTime pgtype.Timestamp `json:"start_time"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
Source string `json:"source"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
}
func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error {
@ -650,6 +756,7 @@ func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error
arg.IsLive,
arg.Status,
arg.Source,
arg.DefaultWinningUpperLimit,
)
return err
}

View File

@ -96,10 +96,14 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
}
const GetAllLeaguesWithSettings = `-- name: GetAllLeaguesWithSettings :many
SELECT id, name, img_url, country_code, bet365_id, sport_id, default_is_active, default_is_featured, company_id, is_active, is_featured, updated_at
FROM league_with_settings
WHERE (company_id = $1)
AND (
SELECT l.id, l.name, l.img_url, l.country_code, l.bet365_id, l.sport_id, l.default_is_active, l.default_is_featured,
cls.company_id,
COALESCE(cls.is_active, l.default_is_active) AS is_active,
COALESCE(cls.is_featured, l.default_is_featured) AS is_featured,
cls.updated_at
FROM leagues l
LEFT JOIN company_league_settings cls ON l.id = cls.league_id AND company_id = $1
WHERE (
country_code = $2
OR $2 IS NULL
)
@ -117,7 +121,6 @@ WHERE (company_id = $1)
)
AND (
name ILIKE '%' || $6 || '%'
OR league_name ILIKE '%' || $6 || '%'
OR $6 IS NULL
)
ORDER BY is_featured DESC,
@ -136,7 +139,22 @@ type GetAllLeaguesWithSettingsParams struct {
Limit pgtype.Int4 `json:"limit"`
}
func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeaguesWithSettingsParams) ([]LeagueWithSetting, error) {
type GetAllLeaguesWithSettingsRow struct {
ID int64 `json:"id"`
Name string `json:"name"`
ImgUrl pgtype.Text `json:"img_url"`
CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"`
SportID int32 `json:"sport_id"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeaguesWithSettingsParams) ([]GetAllLeaguesWithSettingsRow, error) {
rows, err := q.db.Query(ctx, GetAllLeaguesWithSettings,
arg.CompanyID,
arg.CountryCode,
@ -151,9 +169,9 @@ func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeagu
return nil, err
}
defer rows.Close()
var items []LeagueWithSetting
var items []GetAllLeaguesWithSettingsRow
for rows.Next() {
var i LeagueWithSetting
var i GetAllLeaguesWithSettingsRow
if err := rows.Scan(
&i.ID,
&i.Name,

View File

@ -321,7 +321,7 @@ type Event struct {
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"`
}
@ -356,7 +356,7 @@ type EventWithCountry struct {
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"`
LeagueCc pgtype.Text `json:"league_cc"`
}
@ -385,9 +385,9 @@ type EventWithSetting struct {
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"`
CompanyID int64 `json:"company_id"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
@ -447,7 +447,7 @@ type LeagueWithSetting struct {
SportID int32 `json:"sport_id"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
CompanyID int64 `json:"company_id"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
@ -520,7 +520,7 @@ type OddsMarketWithSetting struct {
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID int64 `json:"company_id"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`

View File

@ -68,9 +68,22 @@ func (q *Queries) GetAllOdds(ctx context.Context, arg GetAllOddsParams) ([]OddsM
}
const GetAllOddsWithSettings = `-- name: GetAllOddsWithSettings :many
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 company_id = $1
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id
AND company_id = $1
LIMIT $3 OFFSET $2
`
@ -80,15 +93,31 @@ type GetAllOddsWithSettingsParams struct {
Limit pgtype.Int4 `json:"limit"`
}
func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWithSettingsParams) ([]OddsMarketWithSetting, error) {
type GetAllOddsWithSettingsRow struct {
ID int64 `json:"id"`
EventID string `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWithSettingsParams) ([]GetAllOddsWithSettingsRow, error) {
rows, err := q.db.Query(ctx, GetAllOddsWithSettings, arg.CompanyID, arg.Offset, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OddsMarketWithSetting
var items []GetAllOddsWithSettingsRow
for rows.Next() {
var i OddsMarketWithSetting
var i GetAllOddsWithSettingsRow
if err := rows.Scan(
&i.ID,
&i.EventID,
@ -247,10 +276,23 @@ func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDPa
}
const GetOddsWithSettingsByEventID = `-- name: GetOddsWithSettingsByEventID :many
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 event_id = $1
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id
AND company_id = $2
WHERE event_id = $1
LIMIT $4 OFFSET $3
`
@ -261,7 +303,23 @@ type GetOddsWithSettingsByEventIDParams struct {
Limit pgtype.Int4 `json:"limit"`
}
func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsWithSettingsByEventIDParams) ([]OddsMarketWithSetting, error) {
type GetOddsWithSettingsByEventIDRow struct {
ID int64 `json:"id"`
EventID string `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsWithSettingsByEventIDParams) ([]GetOddsWithSettingsByEventIDRow, error) {
rows, err := q.db.Query(ctx, GetOddsWithSettingsByEventID,
arg.EventID,
arg.CompanyID,
@ -272,9 +330,9 @@ func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsW
return nil, err
}
defer rows.Close()
var items []OddsMarketWithSetting
var items []GetOddsWithSettingsByEventIDRow
for rows.Next() {
var i OddsMarketWithSetting
var i GetOddsWithSettingsByEventIDRow
if err := rows.Scan(
&i.ID,
&i.EventID,
@ -301,10 +359,23 @@ func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsW
}
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
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id
AND company_id = $2
WHERE o.id = $1
`
type GetOddsWithSettingsByIDParams struct {
@ -312,9 +383,25 @@ type GetOddsWithSettingsByIDParams struct {
CompanyID int64 `json:"company_id"`
}
func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSettingsByIDParams) (OddsMarketWithSetting, error) {
type GetOddsWithSettingsByIDRow struct {
ID int64 `json:"id"`
EventID string `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSettingsByIDParams) (GetOddsWithSettingsByIDRow, error) {
row := q.db.QueryRow(ctx, GetOddsWithSettingsByID, arg.ID, arg.CompanyID)
var i OddsMarketWithSetting
var i GetOddsWithSettingsByIDRow
err := row.Scan(
&i.ID,
&i.EventID,
@ -334,11 +421,24 @@ func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSe
}
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
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id
AND company_id = $3
WHERE market_id = $1
AND event_id = $2
AND company_id = $3
`
type GetOddsWithSettingsByMarketIDParams struct {
@ -347,9 +447,25 @@ type GetOddsWithSettingsByMarketIDParams struct {
CompanyID int64 `json:"company_id"`
}
func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (OddsMarketWithSetting, error) {
type GetOddsWithSettingsByMarketIDRow struct {
ID int64 `json:"id"`
EventID string `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (GetOddsWithSettingsByMarketIDRow, error) {
row := q.db.QueryRow(ctx, GetOddsWithSettingsByMarketID, arg.MarketID, arg.EventID, arg.CompanyID)
var i OddsMarketWithSetting
var i GetOddsWithSettingsByMarketIDRow
err := row.Scan(
&i.ID,
&i.EventID,

View File

@ -235,6 +235,7 @@ func ConvertDBBetWithOutcomes(bet dbgen.BetWithOutcome) GetBet {
return GetBet{
ID: bet.ID,
CompanyID: bet.CompanyID,
Amount: Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: OutcomeStatus(bet.Status),

View File

@ -69,7 +69,7 @@ type BaseEvent struct {
IsMonitored bool
DefaultIsFeatured bool
DefaultIsActive bool
DefaultWinningUpperLimit int32
DefaultWinningUpperLimit int64
Score ValidString
MatchMinute ValidInt
TimerStatus ValidString
@ -97,7 +97,7 @@ type BaseEventRes struct {
IsMonitored bool `json:"is_monitored"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
Score string `json:"score"`
MatchMinute int `json:"match_minute"`
TimerStatus string `json:"timer_status"`
@ -128,7 +128,7 @@ type EventWithSettings struct {
WinningUpperLimit int32
DefaultIsFeatured bool
DefaultIsActive bool
DefaultWinningUpperLimit int32
DefaultWinningUpperLimit int64
Score ValidString
MatchMinute ValidInt
TimerStatus ValidString
@ -140,21 +140,22 @@ type EventWithSettings struct {
}
type CreateEvent struct {
ID 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
ID 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 {
@ -179,7 +180,7 @@ type EventWithSettingsRes struct {
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"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
Score string `json:"score,omitempty"`
MatchMinute int `json:"match_minute,omitempty"`
TimerStatus string `json:"timer_status,omitempty"`
@ -279,21 +280,22 @@ func ConvertDBEvents(events []dbgen.EventWithCountry) []BaseEvent {
func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
return dbgen.InsertEventParams{
ID: e.ID,
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),
ID: e.ID,
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,
}
}

View File

@ -148,7 +148,7 @@ func ConvertDBLeagueWithSetting(lws dbgen.LeagueWithSetting) LeagueWithSettings
return LeagueWithSettings{
ID: lws.ID,
Name: lws.Name,
CompanyID: lws.CompanyID,
CompanyID: lws.CompanyID.Int64,
CountryCode: ValidString{
Value: lws.CountryCode.String,
Valid: lws.CountryCode.Valid,

View File

@ -73,7 +73,7 @@ type Notification struct {
RecipientID int64 `json:"recipient_id"`
Type NotificationType `json:"type"`
Level NotificationLevel `json:"level"`
ErrorSeverity *NotificationErrorSeverity `json:"error_severity"`
ErrorSeverity NotificationErrorSeverity `json:"error_severity"`
Reciever NotificationRecieverSide `json:"reciever"`
IsRead bool `json:"is_read"`
DeliveryStatus NotificationDeliveryStatus `json:"delivery_status,omitempty"`

View File

@ -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 ValidInt64 `json:"id"`
ID ValidInt64 `json:"id"`
Name string `json:"name"`
Odds []json.RawMessage `json:"odds"`
Header string `json:"header,omitempty"`

View File

@ -23,6 +23,7 @@ type SettingList struct {
TotalWinningLimit Currency `json:"total_winning_limit"`
AmountForBetReferral Currency `json:"amount_for_bet_referral"`
CashbackAmountCap Currency `json:"cashback_amount_cap"`
DefaultWinningLimit int64 `json:"default_winning_limit"`
}
type SettingListRes struct {
@ -33,6 +34,7 @@ type SettingListRes struct {
TotalWinningLimit float32 `json:"total_winning_limit"`
AmountForBetReferral float32 `json:"amount_for_bet_referral"`
CashbackAmountCap float32 `json:"cashback_amount_cap"`
DefaultWinningLimit int64 `json:"default_winning_limit"`
}
func ConvertSettingListRes(settings SettingList) SettingListRes {
@ -44,6 +46,7 @@ func ConvertSettingListRes(settings SettingList) SettingListRes {
TotalWinningLimit: settings.TotalWinningLimit.Float32(),
AmountForBetReferral: settings.AmountForBetReferral.Float32(),
CashbackAmountCap: settings.CashbackAmountCap.Float32(),
DefaultWinningLimit: settings.DefaultWinningLimit,
}
}
@ -55,6 +58,18 @@ type SaveSettingListReq struct {
TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"`
AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"`
CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"`
DefaultWinningLimit *int64 `json:"default_winning_limit,omitempty"`
}
type ValidSettingList struct {
SMSProvider ValidString
MaxNumberOfOutcomes ValidInt64
BetAmountLimit ValidCurrency
DailyTicketPerIP ValidInt64
TotalWinningLimit ValidCurrency
AmountForBetReferral ValidCurrency
CashbackAmountCap ValidCurrency
DefaultWinningLimit ValidInt64
}
func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList {
@ -66,19 +81,10 @@ func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList {
TotalWinningLimit: ConvertFloat32PtrToCurrency(settings.TotalWinningLimit),
AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral),
CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap),
DefaultWinningLimit: ConvertInt64Ptr(settings.DefaultWinningLimit),
}
}
type ValidSettingList struct {
SMSProvider ValidString
MaxNumberOfOutcomes ValidInt64
BetAmountLimit ValidCurrency
DailyTicketPerIP ValidInt64
TotalWinningLimit ValidCurrency
AmountForBetReferral ValidCurrency
CashbackAmountCap ValidCurrency
}
// Always make sure to run the validation before converting this
func (vsl *ValidSettingList) ToSettingList() SettingList {
return SettingList{
@ -89,6 +95,7 @@ func (vsl *ValidSettingList) ToSettingList() SettingList {
TotalWinningLimit: Currency(vsl.TotalWinningLimit.Value),
AmountForBetReferral: Currency(vsl.AmountForBetReferral.Value),
CashbackAmountCap: Currency(vsl.CashbackAmountCap.Value),
DefaultWinningLimit: vsl.DefaultWinningLimit.Value,
}
}
@ -104,6 +111,7 @@ func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 {
return map[string]*ValidInt64{
"max_number_of_outcomes": &vsl.MaxNumberOfOutcomes,
"daily_ticket_limit": &vsl.DailyTicketPerIP,
"default_winning_limit": &vsl.DefaultWinningLimit,
}
}

View File

@ -2,12 +2,12 @@ package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
)
@ -19,7 +19,7 @@ func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany)
for {
_, err := s.queries.GetCompanyIDUsingSlug(ctx, uniqueSlug)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
if errors.Is(err, pgx.ErrNoRows) {
// slug is unique
break
} else {

View File

@ -16,7 +16,6 @@ func (s *Store) SaveEvent(ctx context.Context, e domain.CreateEvent) error {
return s.queries.InsertEvent(ctx, domain.ConvertCreateEvent(e))
}
func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) {
return s.queries.ListLiveEvents(ctx)
}
@ -86,6 +85,7 @@ func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filt
FirstStartTime: filter.FirstStartTime.ToPG(),
LastStartTime: filter.LastStartTime.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
IsFeatured: filter.Featured.ToPG(),
})
if err != nil {
@ -100,13 +100,69 @@ func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filt
FirstStartTime: filter.FirstStartTime.ToPG(),
LastStartTime: filter.LastStartTime.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
IsFeatured: filter.Featured.ToPG(),
})
if err != nil {
return nil, 0, err
}
numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value))
return domain.ConvertDBEventWithSettings(events), int64(numberOfPages), nil
result := make([]domain.EventWithSettings, len(events))
for i, event := range events {
result[i] = domain.EventWithSettings{
ID: event.ID,
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: domain.ValidString{
Value: event.LeagueCc.String,
Valid: event.LeagueCc.Valid,
},
StartTime: event.StartTime.Time.UTC(),
Source: domain.EventSource(event.Source),
Status: domain.EventStatus(event.Status),
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
DefaultIsFeatured: event.DefaultIsFeatured,
DefaultIsActive: event.DefaultIsActive,
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
Score: domain.ValidString{
Value: event.Score.String,
Valid: event.Score.Valid,
},
MatchMinute: domain.ValidInt{
Value: int(event.MatchMinute.Int32),
Valid: event.MatchMinute.Valid,
},
TimerStatus: domain.ValidString{
Value: event.TimerStatus.String,
Valid: event.TimerStatus.Valid,
},
AddedTime: domain.ValidInt{
Value: int(event.AddedTime.Int32),
Valid: event.AddedTime.Valid,
},
MatchPeriod: domain.ValidInt{
Value: int(event.MatchPeriod.Int32),
Valid: event.MatchPeriod.Valid,
},
IsLive: event.IsLive,
UpdatedAt: event.UpdatedAt.Time,
FetchedAt: event.FetchedAt.Time,
}
}
return result, int64(numberOfPages), nil
}
func (s *Store) GetUpcomingEventByID(ctx context.Context, ID string) (domain.BaseEvent, error) {
event, err := s.queries.GetUpcomingByID(ctx, ID)
@ -125,7 +181,56 @@ func (s *Store) GetEventWithSettingByID(ctx context.Context, ID string, companyI
return domain.EventWithSettings{}, err
}
return domain.ConvertDBEventWithSetting(event), nil
res := domain.EventWithSettings{
ID: event.ID,
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: domain.ValidString{
Value: event.LeagueCc.String,
Valid: event.LeagueCc.Valid,
},
StartTime: event.StartTime.Time.UTC(),
Source: domain.EventSource(event.Source),
Status: domain.EventStatus(event.Status),
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
DefaultIsFeatured: event.DefaultIsFeatured,
DefaultIsActive: event.DefaultIsActive,
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
Score: domain.ValidString{
Value: event.Score.String,
Valid: event.Score.Valid,
},
MatchMinute: domain.ValidInt{
Value: int(event.MatchMinute.Int32),
Valid: event.MatchMinute.Valid,
},
TimerStatus: domain.ValidString{
Value: event.TimerStatus.String,
Valid: event.TimerStatus.Valid,
},
AddedTime: domain.ValidInt{
Value: int(event.AddedTime.Int32),
Valid: event.AddedTime.Valid,
},
MatchPeriod: domain.ValidInt{
Value: int(event.MatchPeriod.Int32),
Valid: event.MatchPeriod.Valid,
},
IsLive: event.IsLive,
UpdatedAt: event.UpdatedAt.Time,
FetchedAt: event.FetchedAt.Time,
}
return res, nil
}
func (s *Store) UpdateFinalScore(ctx context.Context, eventID, fullScore string, status domain.EventStatus) error {
params := dbgen.UpdateMatchResultParams{
@ -173,7 +278,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.SaveEventSettings(ctx, domain.ConvertUpdateEventSettings(event));
return s.queries.SaveEventSettings(ctx, domain.ConvertUpdateEventSettings(event))
}
func (s *Store) DeleteEvent(ctx context.Context, eventID string) error {

View File

@ -51,13 +51,39 @@ func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, fil
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
IsFeatured: filter.IsFeatured.ToPG(),
IsActive: filter.IsActive.ToPG(),
})
if err != nil {
return nil, err
}
return domain.ConvertDBLeagueWithSettings(l), nil
result := make([]domain.LeagueWithSettings, len(l))
for i, league := range l {
result[i] = domain.LeagueWithSettings{
ID: league.ID,
Name: league.Name,
CompanyID: league.CompanyID.Int64,
CountryCode: domain.ValidString{
Value: league.CountryCode.String,
Valid: league.CountryCode.Valid,
},
Bet365ID: domain.ValidInt32{
Value: league.Bet365ID.Int32,
Valid: league.Bet365ID.Valid,
},
IsActive: league.IsActive,
SportID: league.SportID,
IsFeatured: league.IsFeatured,
UpdatedAt: league.UpdatedAt.Time,
DefaultIsActive: league.DefaultIsActive,
DefaultIsFeatured: league.DefaultIsFeatured,
}
}
return result, nil
}
func (s *Store) CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) {

View File

@ -39,8 +39,8 @@ func (s *Store) DisconnectWebSocket(recipientID int64) {
func (r *Repository) CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error) {
var errorSeverity pgtype.Text
if notification.ErrorSeverity != nil {
errorSeverity.String = string(*notification.ErrorSeverity)
if notification.ErrorSeverity != "" {
errorSeverity.String = string(notification.ErrorSeverity)
errorSeverity.Valid = true
}
@ -155,10 +155,12 @@ func (r *Repository) ListRecipientIDs(ctx context.Context, receiver domain.Notif
}
func (r *Repository) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification {
var errorSeverity *domain.NotificationErrorSeverity
var errorSeverity domain.NotificationErrorSeverity
if dbNotif.ErrorSeverity.Valid {
s := domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String)
errorSeverity = &s
errorSeverity = domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String)
} else {
errorSeverity = ""
}
var deliveryChannel domain.DeliveryChannel
@ -317,8 +319,6 @@ func (s *Store) CountUnreadNotifications(ctx context.Context, userID int64) (int
return count, nil
}
// func (s *Store) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) {
// dbNotifications, err := s.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{
// Limit: int32(limit),

View File

@ -88,23 +88,46 @@ func (s *Store) GetAllOddsWithSettings(ctx context.Context, companyID int64, fil
return nil, err
}
domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds)
// domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds)
// if err != nil {
// return nil, err
// }
if err != nil {
return nil, err
result := make([]domain.OddMarketWithSettings, len(odds))
for i, o := range odds {
var rawOdds []json.RawMessage
if len(o.RawOdds) > 0 {
if err := json.Unmarshal(o.RawOdds, &rawOdds); err != nil {
return nil, err
}
} else {
rawOdds = []json.RawMessage{} // explicit empty slice
}
result[i] = domain.OddMarketWithSettings{
ID: o.ID,
EventID: o.EventID,
MarketType: o.MarketType,
MarketName: o.MarketName,
MarketCategory: o.MarketCategory,
MarketID: o.MarketID,
RawOdds: rawOdds,
FetchedAt: o.FetchedAt.Time,
ExpiresAt: o.ExpiresAt.Time,
IsActive: o.IsActive,
}
}
return domainOdds, nil
return result, 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)
convertedOdd, err := domain.ConvertDBOddMarket(odd)
if err != nil {
return domain.OddMarket{}, err
@ -141,18 +164,40 @@ func (s *Store) GetOddsWithSettingsByMarketID(ctx context.Context, marketID stri
return domain.OddMarketWithSettings{}, err
}
convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds)
// convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds)
if err != nil {
return domain.OddMarketWithSettings{}, err
// if err != nil {
// return domain.OddMarketWithSettings{}, err
// }
var rawOdds []json.RawMessage
if len(odds.RawOdds) > 0 {
if err := json.Unmarshal(odds.RawOdds, &rawOdds); err != nil {
return domain.OddMarketWithSettings{}, err
}
} else {
rawOdds = []json.RawMessage{} // explicit empty slice
}
return convertedOdd, nil
converted := domain.OddMarketWithSettings{
ID: odds.ID,
EventID: odds.EventID,
MarketType: odds.MarketType,
MarketName: odds.MarketName,
MarketCategory: odds.MarketCategory,
MarketID: odds.MarketID,
RawOdds: rawOdds,
FetchedAt: odds.FetchedAt.Time,
ExpiresAt: odds.ExpiresAt.Time,
IsActive: odds.IsActive,
}
return converted, 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,
ID: ID,
CompanyID: companyID,
})
@ -160,12 +205,35 @@ func (s *Store) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID
return domain.OddMarketWithSettings{}, err
}
convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds)
// convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds)
if err != nil {
return domain.OddMarketWithSettings{}, err
// if err != nil {
// return domain.OddMarketWithSettings{}, err
// }
var rawOdds []json.RawMessage
if len(odds.RawOdds) > 0 {
if err := json.Unmarshal(odds.RawOdds, &rawOdds); err != nil {
return domain.OddMarketWithSettings{}, err
}
} else {
rawOdds = []json.RawMessage{} // explicit empty slice
}
return convertedOdd, nil
converted := domain.OddMarketWithSettings{
ID: odds.ID,
EventID: odds.EventID,
MarketType: odds.MarketType,
MarketName: odds.MarketName,
MarketCategory: odds.MarketCategory,
MarketID: odds.MarketID,
RawOdds: rawOdds,
FetchedAt: odds.FetchedAt.Time,
ExpiresAt: odds.ExpiresAt.Time,
IsActive: odds.IsActive,
}
return converted, nil
}
func (s *Store) GetOddsByEventID(ctx context.Context, upcomingID string, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) {
@ -208,12 +276,37 @@ func (s *Store) GetOddsWithSettingsByEventID(ctx context.Context, upcomingID str
}
// Map the results to domain.Odd
domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds)
if err != nil {
return nil, err
// domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds)
// if err != nil {
// return nil, err
// }
result := make([]domain.OddMarketWithSettings, len(odds))
for i, o := range odds {
var rawOdds []json.RawMessage
if len(o.RawOdds) > 0 {
if err := json.Unmarshal(o.RawOdds, &rawOdds); err != nil {
return nil, err
}
} else {
rawOdds = []json.RawMessage{} // explicit empty slice
}
result[i] = domain.OddMarketWithSettings{
ID: o.ID,
EventID: o.EventID,
MarketType: o.MarketType,
MarketName: o.MarketName,
MarketCategory: o.MarketCategory,
MarketID: o.MarketID,
RawOdds: rawOdds,
FetchedAt: o.FetchedAt.Time,
ExpiresAt: o.ExpiresAt.Time,
IsActive: o.IsActive,
}
}
return domainOdds, nil
return result, nil
}
func (s *Store) DeleteOddsForEvent(ctx context.Context, eventID string) error {

View File

@ -2,6 +2,7 @@ package repository
import (
"context"
"fmt"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -104,8 +105,10 @@ func (s *Store) GetAllShopBet(ctx context.Context, filter domain.ShopBetFilter)
func (s *Store) GetShopBetByID(ctx context.Context, id int64) (domain.ShopBetDetail, error) {
bet, err := s.queries.GetShopBetByID(ctx, id)
if err != nil {
fmt.Printf("GetShopBetByID Repo BetID %d err %v \n", id, err.Error())
return domain.ShopBetDetail{}, err
}
return convertDBShopBetDetail(bet), nil
}

View File

@ -41,6 +41,7 @@ func convertDBTicketOutcomes(ticket dbgen.TicketWithOutcome) domain.GetTicket {
}
return domain.GetTicket{
ID: ticket.ID,
CompanyID: ticket.CompanyID,
Amount: domain.Currency(ticket.Amount),
TotalOdds: ticket.TotalOdds,
Outcomes: outcomes,

View File

@ -283,8 +283,10 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
)
return domain.CreateBetRes{}, err
}
if count >= 2 {
return domain.CreateBetRes{}, fmt.Errorf("bet already placed twice")
// TODO: Make this a setting
if role == domain.RoleCustomer && count >= 10 {
return domain.CreateBetRes{}, fmt.Errorf("max user limit for single outcome")
}
fastCode := helpers.GenerateFastCode()
@ -340,17 +342,20 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
return domain.CreateBetRes{}, err
}
if branch.BranchManagerID != userID {
s.mongoLogger.Warn("unauthorized branch for branch manager",
zap.Int64("branch_id", *req.BranchID),
zap.Error(err),
)
return domain.CreateBetRes{}, err
if role == domain.RoleBranchManager {
if branch.BranchManagerID != userID {
s.mongoLogger.Warn("unauthorized branch for branch manager",
zap.Int64("branch_id", *req.BranchID),
zap.Error(err),
)
return domain.CreateBetRes{}, err
}
}
if branch.CompanyID == companyID {
if branch.CompanyID != companyID {
s.mongoLogger.Warn("unauthorized company",
zap.Int64("branch_id", *req.BranchID),
zap.Int64("branch_company_id", branch.CompanyID),
zap.Int64("company_id", companyID),
zap.Error(err),
)
}
@ -1073,8 +1078,6 @@ func (s *Service) SendErrorStatusNotification(ctx context.Context, status domain
message = "We have encounter an error with your bet. We will fix it as soon as we can"
}
errorSeverityLevel := domain.NotificationErrorSeverityFatal
betNotification := &domain.Notification{
RecipientID: userID,
DeliveryStatus: domain.DeliveryStatusPending,
@ -1088,7 +1091,7 @@ func (s *Service) SendErrorStatusNotification(ctx context.Context, status domain
Message: message,
},
Priority: 1,
ErrorSeverity: &errorSeverityLevel,
ErrorSeverity: domain.NotificationErrorSeverityHigh,
Metadata: fmt.Appendf(nil, `{
"status":%v
"more": %v
@ -1117,9 +1120,8 @@ func (s *Service) SendAdminErrorAlertNotification(ctx context.Context, status do
message = "We have encounter an error with bet. We will fix it as soon as we can"
}
errorSeverity := domain.NotificationErrorSeverityHigh
betNotification := &domain.Notification{
ErrorSeverity: &errorSeverity,
ErrorSeverity: domain.NotificationErrorSeverityHigh,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
@ -1322,7 +1324,6 @@ func (s *Service) ProcessBetCashback(ctx context.Context) error {
return err
}
for _, bet := range bets {
shouldProcess := true
loseCount := 0

View File

@ -14,6 +14,8 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings"
"github.com/jackc/pgx/v5"
"go.uber.org/zap"
// "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
)
@ -21,13 +23,15 @@ import (
type service struct {
token string
store *repository.Store
settingSvc settings.Service
mongoLogger *zap.Logger
}
func New(token string, store *repository.Store, mongoLogger *zap.Logger) Service {
func New(token string, store *repository.Store, settingSvc settings.Service, mongoLogger *zap.Logger) Service {
return &service{
token: token,
store: store,
settingSvc: settingSvc,
mongoLogger: mongoLogger,
}
}
@ -206,22 +210,31 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error {
}
func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_url string, source domain.EventSource) {
const pageLimit int = 200
sportIDs := []int{1, 18, 17, 3, 83, 15, 12, 19, 8, 16, 91}
// sportIDs := []int{1}
settingsList, err := s.settingSvc.GetGlobalSettingList(ctx)
if err != nil {
s.mongoLogger.Error("Failed to fetch event data for page", zap.Error(err))
return
}
const pageLimit int = 1
// sportIDs := []int{1, 18, 17, 3, 83, 15, 12, 19, 8, 16, 91}
sportIDs := []int{1}
var skippedLeague []string
var totalEvents = 0
nilAway := 0
for sportIndex, sportID := range sportIDs {
var totalPages int = 1
var page int = 0
var count int = 0
var skippedLeague []string
var totalEvents = 0
var pageCount int = 0
var sportEvents = 0
logger := s.mongoLogger.With(
zap.String("source", string(source)),
zap.Int("sport_id", sportID),
zap.String("sport_name", domain.Sport(sportID).String()),
zap.Int("count", count),
zap.Int("totalEvents", totalEvents),
zap.Int("count", pageCount),
zap.Int("Skipped leagues", len(skippedLeague)),
)
for page <= totalPages {
@ -301,34 +314,33 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur
// }
event := domain.CreateEvent{
ID: ev.ID,
SportID: convertInt32(ev.SportID),
MatchName: "",
HomeTeam: ev.Home.Name,
AwayTeam: "", // handle nil safely
HomeTeamID: convertInt64(ev.Home.ID),
AwayTeamID: 0,
HomeTeamImage: "",
AwayTeamImage: "",
LeagueID: convertInt64(ev.League.ID),
LeagueName: ev.League.Name,
StartTime: time.Unix(startUnix, 0).UTC(),
Source: source,
IsLive: false,
Status: domain.STATUS_PENDING,
ID: ev.ID,
SportID: convertInt32(ev.SportID),
HomeTeam: ev.Home.Name,
AwayTeam: "", // handle nil safely
HomeTeamID: convertInt64(ev.Home.ID),
AwayTeamID: 0,
LeagueID: convertInt64(ev.League.ID),
LeagueName: ev.League.Name,
StartTime: time.Unix(startUnix, 0).UTC(),
Source: source,
IsLive: false,
Status: domain.STATUS_PENDING,
DefaultWinningUpperLimit: settingsList.DefaultWinningLimit,
}
if ev.Away != nil {
dataLogger.Info("event away is empty")
event.AwayTeam = ev.Away.Name
event.AwayTeamID = convertInt64(ev.Away.ID)
event.MatchName = ev.Home.Name + " vs " + ev.Away.Name
} else {
nilAway += 1
}
ok, err := s.CheckAndInsertEventHistory(ctx, event)
ok, _ := s.CheckAndInsertEventHistory(ctx, event)
if err != nil {
dataLogger.Error("failed to check and insert event history", zap.Error(err))
}
// if err != nil {
// dataLogger.Error("failed to check and insert event history", zap.Error(err))
// }
if ok {
dataLogger.Info("event history has been recorded")
@ -338,7 +350,8 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur
if err != nil {
dataLogger.Error("failed to save upcoming event", zap.Error(err))
}
totalEvents += 1
sportEvents += 1
}
// log.Printf("⚠️ Skipped leagues %v", len(skippedLeague))
@ -346,26 +359,26 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur
totalPages = data.Pager.Total / data.Pager.PerPage
if count >= pageLimit {
if pageCount >= pageLimit {
break
}
if page > totalPages {
break
}
count++
pageCount++
}
s.mongoLogger.Info(
"Successfully fetched upcoming events",
zap.String("source", string(source)),
zap.Int("totalEvents", totalEvents),
zap.Int("sport_id", sportID),
zap.String("sport_name", domain.Sport(sportID).String()),
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Int("Skipped leagues", len(skippedLeague)),
)
logger.Info("Completed adding sport", zap.Int("number_of_events_in_sport", sportEvents))
totalEvents += sportEvents
}
s.mongoLogger.Info(
"Successfully fetched upcoming events",
zap.String("source", string(source)),
zap.Int("totalEvents", totalEvents),
zap.Int("Skipped leagues", len(skippedLeague)),
zap.Int("Events with empty away data", nilAway),
)
}
func (s *service) CheckAndInsertEventHistory(ctx context.Context, event domain.CreateEvent) (bool, error) {
@ -379,7 +392,9 @@ func (s *service) CheckAndInsertEventHistory(ctx context.Context, event domain.C
)
if err != nil {
eventLogger.Error("failed to get event is_monitored", zap.Error(err))
if err != pgx.ErrNoRows {
eventLogger.Info("failed to get event is_monitored", zap.Error(err))
}
return false, err
}
@ -478,7 +493,7 @@ func (s *service) GetEventsWithSettings(ctx context.Context, companyID int64, fi
return s.store.GetEventsWithSettings(ctx, companyID, filter)
}
func (s *service) GetEventWithSettingByID(ctx context.Context, ID string, companyID int64) (domain.EventWithSettings, error) {
func (s *service) GetEventWithSettingByID(ctx context.Context, ID string, companyID int64) (domain.EventWithSettings, error) {
return s.store.GetEventWithSettingByID(ctx, ID, companyID)
}
func (s *service) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error {

View File

@ -334,14 +334,22 @@ func (s *Service) SendNotificationSMS(ctx context.Context, recipientID int64, me
}
if !user.PhoneVerified {
return fmt.Errorf("Cannot send notification to unverified phone number")
return fmt.Errorf("cannot send notification to unverified phone number")
}
if user.PhoneNumber == "" {
return fmt.Errorf("Phone Number is invalid")
return fmt.Errorf("phone Number is invalid")
}
err = s.messengerSvc.SendSMS(ctx, user.PhoneNumber, message, user.CompanyID)
if err != nil {
s.mongoLogger.Error("[NotificationSvc.HandleNotification] Failed to send notification SMS",
zap.Int64("recipient_id", recipientID),
zap.String("user_phone_number", user.PhoneNumber),
zap.String("message", message),
zap.Int64("company_id", user.CompanyID.Value),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
@ -357,14 +365,22 @@ func (s *Service) SendNotificationEmail(ctx context.Context, recipientID int64,
}
if !user.EmailVerified {
return fmt.Errorf("Cannot send notification to unverified email")
return fmt.Errorf("cannot send notification to unverified email")
}
if user.PhoneNumber == "" {
return fmt.Errorf("Email is invalid")
if user.Email == "" {
return fmt.Errorf("email is invalid")
}
err = s.messengerSvc.SendEmail(ctx, user.PhoneNumber, message, subject)
err = s.messengerSvc.SendEmail(ctx, user.Email, message, subject)
if err != nil {
s.mongoLogger.Error("[NotificationSvc.HandleNotification] Failed to send notification SMS",
zap.Int64("recipient_id", recipientID),
zap.String("user_email", user.Email),
zap.String("message", message),
zap.Int64("company_id", user.CompanyID.Value),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}

View File

@ -541,9 +541,8 @@ func (s *Service) SendAdminResultStatusErrorNotification(
}
headline, message := buildHeadlineAndMessage(counts)
errorSeverity := domain.NotificationErrorSeverityLow
notification := &domain.Notification{
ErrorSeverity: &errorSeverity,
ErrorSeverity: domain.NotificationErrorSeverityHigh,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,

View File

@ -52,6 +52,7 @@ func (s *Service) CreateShopBet(ctx context.Context, userID int64, role domain.R
newBet, err := s.betSvc.PlaceBet(ctx, domain.CreateBetReq{
Outcomes: req.Outcomes,
Amount: req.Amount,
BranchID: branchID,
}, userID, role, *companyID)
if err != nil {
@ -94,6 +95,10 @@ func (s *Service) CreateShopBet(ctx context.Context, userID int64, role domain.R
},
})
if err != nil {
return domain.ShopBet{}, err
}
return s.transactionStore.CreateShopBet(ctx, domain.CreateShopBet{
ShopTransactionID: newTransaction.ID,
CashoutID: cashoutID,

View File

@ -148,7 +148,7 @@ func (s *Service) notifyCashiersForVerification(ctx context.Context, depositID,
Metadata: metadataJSON,
}
if err := s.notificationStore.SendNotification(ctx, notification); err != nil {
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
s.logger.Error("failed to send verification notification",
"cashier_id", cashier.ID,
"error", err)
@ -199,7 +199,7 @@ func (s *Service) notifyCustomerVerificationResult(ctx context.Context, deposit
Metadata: metadataJSON,
}
if err := s.notificationStore.SendNotification(ctx, notification); err != nil {
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
s.logger.Error("failed to send deposit result notification",
"customer_id", deposit.CustomerID,
"error", err)

View File

@ -14,7 +14,6 @@ type Service struct {
walletStore WalletStore
transferStore TransferStore
directDepositStore DirectDepositStore
notificationStore notificationservice.NotificationStore
notificationSvc *notificationservice.Service
userSvc *user.Service
mongoLogger *zap.Logger
@ -26,7 +25,6 @@ func NewService(
walletStore WalletStore,
transferStore TransferStore,
directDepositStore DirectDepositStore,
notificationStore notificationservice.NotificationStore,
notificationSvc *notificationservice.Service,
userSvc *user.Service,
mongoLogger *zap.Logger,
@ -38,7 +36,6 @@ func NewService(
transferStore: transferStore,
directDepositStore: directDepositStore,
// approvalStore: approvalStore,
notificationStore: notificationStore,
notificationSvc: notificationSvc,
userSvc: userSvc,
mongoLogger: mongoLogger,

View File

@ -135,7 +135,7 @@ func (s *Service) SendTransferNotification(ctx context.Context, senderWallet dom
}
// Sender notifications
if err := s.notificationStore.SendNotification(ctx, senderNotify); err != nil {
if err := s.notificationSvc.SendNotification(ctx, senderNotify); err != nil {
s.logger.Error("failed to send sender notification",
"user_id", "",
"error", err)
@ -163,7 +163,7 @@ func (s *Service) SendTransferNotification(ctx context.Context, senderWallet dom
}`, amount, receiverWallet.Balance, receiverWallet.ID)),
}
// Sender notifications
if err := s.notificationStore.SendNotification(ctx, receiverNotify); err != nil {
if err := s.notificationSvc.SendNotification(ctx, receiverNotify); err != nil {
s.logger.Error("failed to send sender notification",
"user_id", "",
"error", err)

View File

@ -304,10 +304,10 @@ func (s *Service) GetAdminNotificationRecipients(ctx context.Context, walletID i
}
func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet) error {
errorSeverity := domain.NotificationErrorSeverityLow
// Send notification to admin team
adminNotification := &domain.Notification{
ErrorSeverity: &errorSeverity,
ErrorSeverity: "low",
IsRead: false,
DeliveryStatus: domain.DeliveryStatusPending,
RecipientID: adminWallet.UserID,
@ -343,7 +343,7 @@ func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWalle
for _, adminID := range adminRecipients {
adminNotification.RecipientID = adminID
if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
s.mongoLogger.Error("failed to send admin notification",
zap.Int64("admin_id", adminID),
zap.Error(err),
@ -353,7 +353,7 @@ func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWalle
adminNotification.DeliveryChannel = domain.DeliveryChannelEmail
if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
s.mongoLogger.Error("failed to send email admin notification",
zap.Int64("admin_id", adminID),
zap.Error(err),
@ -366,11 +366,9 @@ func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWalle
}
func (s *Service) SendAdminWalletInsufficientNotification(ctx context.Context, adminWallet domain.Wallet, amount domain.Currency) error {
errorSeverity := domain.NotificationErrorSeverityLow
// Send notification to admin team
adminNotification := &domain.Notification{
ErrorSeverity: &errorSeverity,
ErrorSeverity: domain.NotificationErrorSeverityLow,
IsRead: false,
DeliveryStatus: domain.DeliveryStatusPending,
RecipientID: adminWallet.UserID,
@ -408,7 +406,7 @@ func (s *Service) SendAdminWalletInsufficientNotification(ctx context.Context, a
}
for _, adminID := range recipients {
adminNotification.RecipientID = adminID
if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
s.mongoLogger.Error("failed to send admin notification",
zap.Int64("admin_id", adminID),
zap.Error(err),
@ -417,7 +415,7 @@ func (s *Service) SendAdminWalletInsufficientNotification(ctx context.Context, a
}
adminNotification.DeliveryChannel = domain.DeliveryChannelEmail
if err := s.notificationStore.SendNotification(ctx, adminNotification); err != nil {
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
s.mongoLogger.Error("failed to send email admin notification",
zap.Int64("admin_id", adminID),
zap.Error(err),
@ -431,10 +429,9 @@ func (s *Service) SendAdminWalletInsufficientNotification(ctx context.Context, a
}
func (s *Service) SendCustomerWalletInsufficientNotification(ctx context.Context, customerWallet domain.Wallet, amount domain.Currency) error {
errorSeverity := domain.NotificationErrorSeverityLow
// Send notification to admin team
customerNotification := &domain.Notification{
ErrorSeverity: &errorSeverity,
ErrorSeverity: domain.NotificationErrorSeverityLow,
IsRead: false,
DeliveryStatus: domain.DeliveryStatusPending,
RecipientID: customerWallet.UserID,
@ -460,7 +457,7 @@ func (s *Service) SendCustomerWalletInsufficientNotification(ctx context.Context
}`, customerWallet.ID, customerWallet.Balance, amount.Float32()),
}
if err := s.notificationStore.SendNotification(ctx, customerNotification); err != nil {
if err := s.notificationSvc.SendNotification(ctx, customerNotification); err != nil {
s.mongoLogger.Error("failed to create customer notification",
zap.Int64("customer_id", customerWallet.UserID),
zap.Error(err),

View File

@ -7,7 +7,6 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
@ -46,6 +45,19 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
res, err := h.CreateBetInternal(c, req, userID, role, companyID.Value)
if err != nil {
switch err {
case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, bet.ErrTotalBalanceNotEnough:
h.mongoLoggerSvc.Info("PlaceBet failed",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Int64("userID", userID),
zap.Int64("companyID", companyID.Value),
zap.String("role", string(role)),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
h.mongoLoggerSvc.Error("Failed to create bet",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Int64("user_id", userID),
@ -200,7 +212,7 @@ func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userI
res, err := h.betSvc.PlaceBet(c.Context(), req, userID, role, companyID)
if err != nil {
switch err {
case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, wallet.ErrBalanceInsufficient:
case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, bet.ErrTotalBalanceNotEnough:
h.mongoLoggerSvc.Info("PlaceBet failed",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Int64("userID", userID),

View File

@ -352,6 +352,7 @@ func (h *Handler) GetTopLeagues(c *fiber.Ctx) error {
Value: true,
Valid: true,
},
})
if err != nil {

View File

@ -208,12 +208,17 @@ func (h *Handler) CreateAndSendNotification(c *fiber.Ctx) error {
// return fiber.NewError(fiber.StatusForbidden, "Unauthorized to send notification to this recipient")
// }
errorSeverity := domain.NotificationErrorSeverityMedium
if req.ErrorSeverity != nil {
errorSeverity = *req.ErrorSeverity
}
notification := &domain.Notification{
ID: "",
RecipientID: req.RecipientID,
Type: req.Type,
Level: req.Level,
ErrorSeverity: req.ErrorSeverity,
ErrorSeverity: errorSeverity,
Reciever: req.Reciever,
IsRead: false,
DeliveryStatus: domain.DeliveryStatusPending,
@ -257,12 +262,17 @@ func (h *Handler) CreateAndSendNotification(c *fiber.Ctx) error {
notificationIDs := make([]string, 0, len(recipients))
for _, user := range recipients {
errorSeverity := domain.NotificationErrorSeverityMedium
if req.ErrorSeverity != nil {
errorSeverity = *req.ErrorSeverity
}
notification := &domain.Notification{
ID: "",
RecipientID: user.ID,
Type: req.Type,
Level: req.Level,
ErrorSeverity: req.ErrorSeverity,
ErrorSeverity: errorSeverity,
Reciever: req.Reciever,
IsRead: false,
DeliveryStatus: domain.DeliveryStatusPending,

View File

@ -65,7 +65,7 @@ func (h *Handler) CreateShopBet(c *fiber.Ctx) error {
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(statusCode, "failed to create shop bet"+err.Error())
return fiber.NewError(statusCode, err.Error())
}
res := domain.ConvertShopBet(shopBet)
@ -464,6 +464,7 @@ func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
// role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
role := c.Locals("role").(domain.Role)
searchQuery := c.Query("query")
searchString := domain.ValidString{
@ -509,6 +510,14 @@ func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
}
}
companyFilter := int64(c.QueryInt("company_id"))
if role == domain.RoleSuperAdmin {
companyID = domain.ValidInt64{
Value: companyFilter,
Valid: companyFilter != 0,
}
}
// Check user role and fetch transactions accordingly
transactions, err := h.transactionSvc.GetAllShopTransactions(c.Context(), domain.ShopTransactionFilter{
CompanyID: companyID,

View File

@ -123,6 +123,10 @@ func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
if ticket.CompanyID != companyID.Value {
h.mongoLoggerSvc.Warn("User attempt to access another company ticket",
zap.Int64("ticketID", id),
zap.Int64("ticket CompanyID", ticket.CompanyID),
zap.Int64("companyID", companyID.Value),
zap.Bool("companyID Valid", companyID.Valid),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),