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,6 +716,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()
`
@ -631,6 +736,7 @@ type InsertEventParams struct {
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
@ -155,6 +155,7 @@ type CreateEvent struct {
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"`
@ -294,6 +295,7 @@ func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
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

@ -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,16 +88,39 @@ 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 {
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
}
return domainOdds, nil
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 result, nil
}
func (s *Store) GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error) {
odd, err := s.queries.GetOddByID(ctx, id)
if err != nil {
@ -141,12 +164,34 @@ 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 {
// 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
}
return convertedOdd, nil
} else {
rawOdds = []json.RawMessage{} // explicit empty slice
}
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) {
@ -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 {
// 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
}
return convertedOdd, nil
} else {
rawOdds = []json.RawMessage{} // explicit empty slice
}
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 {
// 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
}
return domainOdds, nil
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 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,6 +342,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
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),
@ -347,10 +350,12 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
)
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 {
@ -303,32 +316,31 @@ 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,
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++
}
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("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)),
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
}

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()),