enetpulse service + atlas orchestrator

This commit is contained in:
Yared Yemane 2025-09-24 21:00:13 +03:00
commit e41ce709a3
150 changed files with 9049 additions and 3962 deletions

View File

@ -46,6 +46,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/raffle"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/recommendation"
referralservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/referal"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/report"
@ -114,13 +115,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, cfg)
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
@ -132,7 +131,6 @@ func main() {
wallet.WalletStore(store),
wallet.TransferStore(store),
wallet.DirectDepositStore(store),
notificatioStore,
notificationSvc,
userSvc,
domain.MongoDBLogger,
@ -145,17 +143,18 @@ func main() {
leagueSvc := league.New(store)
ticketSvc := ticket.NewService(store, eventSvc, *oddsSvc, domain.MongoDBLogger, *settingSvc, notificationSvc)
betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, *companySvc, *settingSvc, *userSvc, notificationSvc, logger, domain.MongoDBLogger)
resultSvc := result.NewService(store, cfg, logger, domain.MongoDBLogger, *betSvc, *oddsSvc, eventSvc, leagueSvc, notificationSvc, *userSvc)
bonusSvc := bonus.NewService(store)
resultSvc := result.NewService(store, cfg, logger, domain.MongoDBLogger, *betSvc, *oddsSvc, eventSvc, leagueSvc, notificationSvc, messengerSvc, *userSvc)
bonusSvc := bonus.NewService(store, walletSvc, settingSvc, notificationSvc, domain.MongoDBLogger)
referalRepo := repository.NewReferralRepository(store)
vitualGameRepo := repository.NewVirtualGameRepository(store)
recommendationRepo := repository.NewRecommendationRepository(store)
referalSvc := referralservice.New(referalRepo, *walletSvc, store, cfg, logger)
referalSvc := referralservice.New(referalRepo, *walletSvc, *settingSvc, cfg, logger, domain.MongoDBLogger)
raffleSvc := raffle.NewService(store)
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), domain.MongoDBLogger, cfg)
atlasClient := atlas.NewClient(cfg, walletSvc)
atlasVirtualGameService := atlas.New(virtualGameSvc, vitualGameRepo, atlasClient, walletSvc, wallet.TransferStore(store), cfg)
recommendationSvc := recommendation.NewService(recommendationRepo)
@ -285,6 +284,7 @@ func main() {
eventSvc,
leagueSvc,
referalSvc,
raffleSvc,
bonusSvc,
virtualGameSvc,
aleaService,

View File

@ -0,0 +1,379 @@
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Locations Initial Data
INSERT INTO branch_locations (key, value)
VALUES ('addis_ababa', 'Addis Ababa'),
('dire_dawa', 'Dire Dawa'),
('mekelle', 'Mekelle'),
('adama', 'Adama'),
('awassa', 'Awassa'),
('bahir_dar', 'Bahir Dar'),
('gonder', 'Gonder'),
('dessie', 'Dessie'),
('jimma', 'Jimma'),
('jijiga', 'Jijiga'),
('shashamane', 'Shashamane'),
('bishoftu', 'Bishoftu'),
('sodo', 'Sodo'),
('arba_minch', 'Arba Minch'),
('hosaena', 'Hosaena'),
('harar', 'Harar'),
('dilla', 'Dilla'),
('nekemte', 'Nekemte'),
('debre_birhan', 'Debre Birhan'),
('asella', 'Asella'),
('debre_markos', 'Debre Markos'),
('kombolcha', 'Kombolcha'),
('debre_tabor', 'Debre Tabor'),
('adigrat', 'Adigrat'),
('areka', 'Areka'),
('weldiya', 'Weldiya'),
('sebeta', 'Sebeta'),
('burayu', 'Burayu'),
('shire', 'Shire'),
('ambo', 'Ambo'),
('arsi_negele', 'Arsi Negele'),
('aksum', 'Aksum'),
('gambela', 'Gambela'),
('bale_robe', 'Bale Robe'),
('butajira', 'Butajira'),
('batu', 'Batu'),
('boditi', 'Boditi'),
('adwa', 'Adwa'),
('yirgalem', 'Yirgalem'),
('waliso', 'Waliso'),
('welkite', 'Welkite'),
('gode', 'Gode'),
('meki', 'Meki'),
('negele_borana', 'Negele Borana'),
('alaba_kulito', 'Alaba Kulito'),
('alamata,', 'Alamata,'),
('chiro', 'Chiro'),
('tepi', 'Tepi'),
('durame', 'Durame'),
('goba', 'Goba'),
('assosa', 'Assosa'),
('gimbi', 'Gimbi'),
('wukro', 'Wukro'),
('haramaya', 'Haramaya'),
('mizan_teferi', 'Mizan Teferi'),
('sawla', 'Sawla'),
('mojo', 'Mojo'),
('dembi_dolo', 'Dembi Dolo'),
('aleta_wendo', 'Aleta Wendo'),
('metu', 'Metu'),
('mota', 'Mota'),
('fiche', 'Fiche'),
('finote_selam', 'Finote Selam'),
('bule_hora_town', 'Bule Hora Town'),
('bonga', 'Bonga'),
('kobo', 'Kobo'),
('jinka', 'Jinka'),
('dangila', 'Dangila'),
('degehabur', 'Degehabur'),
('bedessa', 'Bedessa'),
('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'),
('max_number_of_outcomes', '30'),
('bet_amount_limit', '10000000'),
('daily_ticket_limit', '50'),
('total_winnings_limit', '1000000'),
('amount_for_bet_referral', '1000000'),
('cashback_amount_cap', '1000'),
('default_winning_limit', '5000000'),
('referral_reward_amount', '10000'),
('cashback_percentage', '0.2'),
('default_max_referrals', '15'),
('minimum_bet_amount', '100'),
('bet_duplicate_limit', '5'),
('send_email_on_bet_finish', 'true'),
('send_sms_on_bet_finish', 'false'),
('welcome_bonus_active', 'false'),
('welcome_bonus_multiplier', '1.5'),
('welcome_bonus_cap', '100000'),
('welcome_bonus_count', '3'),
('welcome_bonus_expiry', '10') ON CONFLICT (key) DO NOTHING;
-- Users
INSERT INTO users (
id,
first_name,
last_name,
email,
phone_number,
password,
role,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
VALUES (
1,
'John',
'Doe',
'john.doe@example.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'customer',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
1
),
(
2,
'Test',
'Admin',
'test.admin@gmail.com',
'0988554466',
crypt('password@123', gen_salt('bf'))::bytea,
'admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
1
),
(
3,
'Samuel',
'Tariku',
'cybersamt@gmail.com',
'0911111111',
crypt('password@123', gen_salt('bf'))::bytea,
'super_admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
),
(
4,
'Kirubel',
'Kibru',
'kirubel.jkl679@gmail.com',
'0911554486',
crypt('password@123', gen_salt('bf'))::bytea,
'super_admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
) ON CONFLICT (id) DO
UPDATE
SET first_name = EXCLUDED.first_name,
last_name = EXCLUDED.last_name,
email = EXCLUDED.email,
phone_number = EXCLUDED.phone_number,
password = EXCLUDED.password,
role = EXCLUDED.role,
email_verified = EXCLUDED.email_verified,
phone_verified = EXCLUDED.phone_verified,
created_at = EXCLUDED.created_at,
updated_at = EXCLUDED.updated_at,
suspended = EXCLUDED.suspended,
company_id = EXCLUDED.company_id;
-- Supported Operations
INSERT INTO supported_operations (id, name, description)
VALUES (1, 'SportBook', 'Sportbook operations'),
(2, 'Virtual', 'Virtual operations') ON CONFLICT (id) DO
UPDATE
SET name = EXCLUDED.name,
description = EXCLUDED.description;
-- Wallets
INSERT INTO wallets (
id,
balance,
is_withdraw,
is_bettable,
is_transferable,
user_id,
type,
currency,
is_active,
created_at,
updated_at
)
VALUES (
1,
10000,
TRUE,
TRUE,
TRUE,
1,
'regular_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
2,
5000,
FALSE,
TRUE,
TRUE,
1,
'static_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
3,
100000000,
TRUE,
TRUE,
TRUE,
2,
'company_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
4,
50000000,
TRUE,
TRUE,
TRUE,
2,
'branch_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
) ON CONFLICT (id) DO
UPDATE
SET balance = EXCLUDED.balance,
is_withdraw = EXCLUDED.is_withdraw,
is_bettable = EXCLUDED.is_bettable,
is_transferable = EXCLUDED.is_transferable,
user_id = EXCLUDED.user_id,
type = EXCLUDED.type,
currency = EXCLUDED.currency,
is_active = EXCLUDED.is_active,
created_at = EXCLUDED.created_at,
updated_at = EXCLUDED.updated_at;
-- Customer Wallets
INSERT INTO customer_wallets (
id,
customer_id,
regular_wallet_id,
static_wallet_id
)
VALUES (1, 1, 1, 2) ON CONFLICT (id) DO
UPDATE
SET customer_id = EXCLUDED.customer_id,
regular_wallet_id = EXCLUDED.regular_wallet_id,
static_wallet_id = EXCLUDED.static_wallet_id;
-- Company
INSERT INTO companies (
id,
name,
slug,
admin_id,
wallet_id,
deducted_percentage,
is_active,
created_at,
updated_at
)
VALUES (
1,
'FortuneBets',
'fortunebets',
2,
3,
0.10,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
) ON CONFLICT (id) DO
UPDATE
SET name = EXCLUDED.name,
slug = EXCLUDED.slug,
admin_id = EXCLUDED.admin_id,
wallet_id = EXCLUDED.wallet_id,
deducted_percentage = EXCLUDED.deducted_percentage,
is_active = EXCLUDED.is_active,
created_at = EXCLUDED.created_at,
updated_at = EXCLUDED.updated_at;
-- Branch
INSERT INTO branches (
id,
name,
location,
wallet_id,
branch_manager_id,
company_id,
is_self_owned,
profit_percent,
is_active,
created_at,
updated_at
)
VALUES (
1,
'Test Branch',
'addis_ababa',
4,
2,
1,
TRUE,
0.10,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
) ON CONFLICT (id) DO
UPDATE
SET name = EXCLUDED.name,
location = EXCLUDED.location,
wallet_id = EXCLUDED.wallet_id,
branch_manager_id = EXCLUDED.branch_manager_id,
company_id = EXCLUDED.company_id,
is_self_owned = EXCLUDED.is_self_owned,
profit_percent = EXCLUDED.profit_percent,
is_active = EXCLUDED.is_active,
created_at = EXCLUDED.created_at,
updated_at = EXCLUDED.updated_at;
-- Bonus
INSERT INTO user_bonuses (
id,
name,
description,
type,
user_id,
reward_amount,
expires_at
)
VALUES (
1,
'Welcome Bonus',
'Awarded for deposit number (1 / 3)',
'welcome_bonus',
1,
1000,
now() + INTERVAL '1 day'
) ON CONFLICT (id) DO
UPDATE
SET name = EXCLUDED.name,
description = EXCLUDED.description,
type = EXCLUDED.type,
user_id = EXCLUDED.user_id,
reward_amount = EXCLUDED.reward_amount,
expires_at = EXCLUDED.expires_at;

148
db/data/002_veli_user.sql Normal file
View File

@ -0,0 +1,148 @@
-- Users
INSERT INTO users (
id,
first_name,
last_name,
email,
phone_number,
password,
role,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
VALUES (
5,
'Test',
'Veli',
'test.veli@example.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'customer',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
1
),
(
6,
'Kirubel',
'Kibru',
'modernkibru@gmail.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'customer',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
1
) ON CONFLICT (id) DO
UPDATE
SET first_name = EXCLUDED.first_name,
last_name = EXCLUDED.last_name,
email = EXCLUDED.email,
phone_number = EXCLUDED.phone_number,
password = EXCLUDED.password,
role = EXCLUDED.role,
email_verified = EXCLUDED.email_verified,
phone_verified = EXCLUDED.phone_verified,
created_at = EXCLUDED.created_at,
updated_at = EXCLUDED.updated_at,
suspended = EXCLUDED.suspended,
company_id = EXCLUDED.company_id;
INSERT INTO wallets (
id,
balance,
is_withdraw,
is_bettable,
is_transferable,
user_id,
type,
currency,
is_active,
created_at,
updated_at
)
VALUES (
5,
10000,
TRUE,
TRUE,
TRUE,
1,
'regular_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
6,
5000,
FALSE,
TRUE,
TRUE,
1,
'static_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
7,
1000000,
TRUE,
TRUE,
TRUE,
1,
'regular_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
8,
5000,
FALSE,
TRUE,
TRUE,
1,
'static_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
) ON CONFLICT (id) DO
UPDATE
SET balance = EXCLUDED.balance,
is_withdraw = EXCLUDED.is_withdraw,
is_bettable = EXCLUDED.is_bettable,
is_transferable = EXCLUDED.is_transferable,
user_id = EXCLUDED.user_id,
type = EXCLUDED.type,
currency = EXCLUDED.currency,
is_active = EXCLUDED.is_active,
created_at = EXCLUDED.created_at,
updated_at = EXCLUDED.updated_at;
-- Customer Wallets
INSERT INTO customer_wallets (
id,
customer_id,
regular_wallet_id,
static_wallet_id
)
VALUES (2, 5, 5, 6),
(3, 6, 7, 8) ON CONFLICT (id) DO
UPDATE
SET customer_id = EXCLUDED.customer_id,
regular_wallet_id = EXCLUDED.regular_wallet_id,
static_wallet_id = EXCLUDED.static_wallet_id;

View File

@ -0,0 +1,31 @@
-- For each table with an id sequence
SELECT setval(
pg_get_serial_sequence('users', 'id'),
COALESCE(MAX(id), 1)
)
FROM users;
SELECT setval(
pg_get_serial_sequence('wallets', 'id'),
COALESCE(MAX(id), 1)
)
FROM wallets;
SELECT setval(
pg_get_serial_sequence('customer_wallets', 'id'),
COALESCE(MAX(id), 1)
)
FROM customer_wallets;
SELECT setval(
pg_get_serial_sequence('companies', 'id'),
COALESCE(MAX(id), 1)
)
FROM companies;
SELECT setval(
pg_get_serial_sequence('branches', 'id'),
COALESCE(MAX(id), 1)
)
FROM branches;
SELECT setval(
pg_get_serial_sequence('supported_operations', 'id'),
COALESCE(MAX(id), 1)
)
FROM supported_operations;

View File

@ -1,222 +0,0 @@
BEGIN;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Users
INSERT INTO users (
id,
first_name,
last_name,
email,
phone_number,
password,
role,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
VALUES (
1,
'John',
'Doe',
'john.doe@example.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'customer',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
),
(
2,
'Test',
'Admin',
'test.admin@gmail.com',
'0988554466',
crypt('password123', gen_salt('bf'))::bytea,
'admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
1
),
(
3,
'Samuel',
'Tariku',
'cybersamt@gmail.com',
'0911111111',
crypt('password@123', gen_salt('bf'))::bytea,
'super_admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
),
(
4,
'Kirubel',
'Kibru',
'kirubel.jkl679@gmail.com',
'0911554486',
crypt('password@123', gen_salt('bf'))::bytea,
'super_admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
),
(
5,
'Test',
'Veli',
'test.veli@example.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'customer',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
);
-- Supported Operations
INSERT INTO supported_operations (id, name, description)
VALUES (1, 'SportBook', 'Sportbook operations'),
(2, 'Virtual', 'Virtual operations');
-- Wallets
INSERT INTO wallets (
id,
balance,
is_withdraw,
is_bettable,
is_transferable,
user_id,
type,
currency,
is_active,
created_at,
updated_at
)
VALUES (
1,
10000,
TRUE,
TRUE,
TRUE,
1,
'regular',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
2,
5000,
FALSE,
TRUE,
TRUE,
1,
'static',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
3,
20000,
TRUE,
TRUE,
TRUE,
2,
'company_main',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
4,
15000,
TRUE,
TRUE,
TRUE,
2,
'branch_main',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
-- Customer Wallets
INSERT INTO customer_wallets (
id,
customer_id,
regular_wallet_id,
static_wallet_id
)
VALUES (1, 1, 1, 2);
-- Company
INSERT INTO companies (
id,
name,
slug,
admin_id,
wallet_id,
deducted_percentage,
is_active,
created_at,
updated_at
)
VALUES (
1,
'FortuneBets',
'fortunebets',
2,
3,
0.10,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
-- Branch
INSERT INTO branches (
id,
name,
location,
wallet_id,
branch_manager_id,
company_id,
is_self_owned,
profit_percent,
is_active,
created_at,
updated_at
)
VALUES (
1,
'Test Branch',
'addis_ababa',
4,
2,
1,
TRUE,
0.10,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
COMMIT;

View File

@ -18,21 +18,24 @@ CREATE TABLE IF NOT EXISTS users (
email IS NOT NULL
OR phone_number IS NOT NULL
),
UNIQUE(email, company_id),
UNIQUE (email, company_id),
UNIQUE (phone_number, company_id)
);
CREATE TABLE IF NOT EXISTS virtual_game_providers (
id BIGSERIAL PRIMARY KEY,
provider_id VARCHAR(100) UNIQUE NOT NULL, -- providerId from Veli Games
provider_name VARCHAR(255) NOT NULL, -- providerName
logo_dark TEXT, -- logoForDark (URL)
logo_light TEXT, -- logoForLight (URL)
enabled BOOLEAN NOT NULL DEFAULT TRUE, -- allow enabling/disabling providers
provider_id VARCHAR(100) UNIQUE NOT NULL,
-- providerId from Veli Games
provider_name VARCHAR(255) NOT NULL,
-- providerName
logo_dark TEXT,
-- logoForDark (URL)
logo_light TEXT,
-- logoForLight (URL)
enabled BOOLEAN NOT NULL DEFAULT TRUE,
-- allow enabling/disabling 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,
@ -41,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,
@ -62,7 +62,14 @@ CREATE TABLE IF NOT EXISTS wallets (
is_bettable BOOLEAN NOT NULL,
is_transferable BOOLEAN NOT NULL,
user_id BIGINT NOT NULL,
type VARCHAR(255) NOT NULL,
type TEXT NOT NULL CHECK (
type IN (
'regular_wallet',
'static_wallet',
'branch_wallet',
'company_wallet'
)
),
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
@ -118,7 +125,7 @@ CREATE TABLE exchange_rates (
to_currency VARCHAR(3) NOT NULL,
rate DECIMAL(19, 6) NOT NULL,
valid_until TIMESTAMP NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
UNIQUE (from_currency, to_currency)
);
CREATE TABLE IF NOT EXISTS bet_outcomes (
@ -186,7 +193,8 @@ CREATE TABLE IF NOT EXISTS wallets (
type VARCHAR(255) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT balance_positve CHECK (balance >= 0)
);
CREATE TABLE IF NOT EXISTS customer_wallets (
id BIGSERIAL PRIMARY KEY,
@ -242,9 +250,9 @@ CREATE TABLE IF NOT EXISTS shop_bets (
cashed_out BOOLEAN DEFAULT FALSE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(shop_transaction_id),
UNIQUE(bet_id),
UNIQUE(cashout_id)
UNIQUE (shop_transaction_id),
UNIQUE (bet_id),
UNIQUE (cashout_id)
);
CREATE TABLE IF NOT EXISTS shop_deposits (
id BIGSERIAL PRIMARY KEY,
@ -254,7 +262,7 @@ CREATE TABLE IF NOT EXISTS shop_deposits (
branch_wallet_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(shop_transaction_id)
UNIQUE (shop_transaction_id)
);
CREATE TABLE IF NOT EXISTS branches (
id BIGSERIAL PRIMARY KEY,
@ -268,7 +276,7 @@ CREATE TABLE IF NOT EXISTS branches (
is_self_owned BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(wallet_id),
UNIQUE (wallet_id),
CONSTRAINT profit_percentage_check CHECK (
profit_percent >= 0
AND profit_percent < 1
@ -285,12 +293,9 @@ CREATE TABLE IF NOT EXISTS branch_cashiers (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
branch_id BIGINT NOT NULL,
UNIQUE(user_id, branch_id)
);
CREATE TABLE IF NOT EXISTS branch_locations (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
UNIQUE (user_id, branch_id)
);
CREATE TABLE IF NOT EXISTS branch_locations (key TEXT PRIMARY KEY, value TEXT NOT NULL);
CREATE TABLE events (
id TEXT PRIMARY KEY,
sport_id INT NOT NULL,
@ -311,21 +316,15 @@ CREATE TABLE events (
match_period INT,
is_live BOOLEAN NOT NULL DEFAULT false,
status TEXT NOT NULL,
fetched_at TIMESTAMP DEFAULT now(),
fetched_at TIMESTAMP DEFAULT now (),
source TEXT NOT NULL DEFAULT 'b365api' CHECK (
source IN (
'b365api',
'bfair',
'1xbet',
'bwin',
'enetpulse'
)
source IN ('b365api', 'bfair', '1xbet', 'bwin', 'enetpulse')
),
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)
UNIQUE (id, source)
);
CREATE TABLE event_history (
id BIGSERIAL PRIMARY KEY,
@ -341,7 +340,7 @@ CREATE TABLE company_event_settings (
is_featured BOOLEAN,
winning_upper_limit INT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(company_id, event_id)
UNIQUE (company_id, event_id)
);
CREATE TABLE odds_market (
id BIGSERIAL PRIMARY KEY,
@ -352,13 +351,13 @@ CREATE TABLE odds_market (
market_id TEXT NOT NULL,
raw_odds JSONB NOT NULL,
default_is_active BOOLEAN NOT NULL DEFAULT true,
fetched_at TIMESTAMP DEFAULT now(),
fetched_at TIMESTAMP DEFAULT now (),
expires_at TIMESTAMP NOT NULL,
UNIQUE (event_id, market_id)
);
CREATE TABLE odd_history (
id BIGSERIAL PRIMARY KEY,
odds_market_id BIGINT NOT NULL REFERENCES odds_market(id),
odds_market_id BIGINT NOT NULL REFERENCES odds_market (id),
raw_odd_id BIGINT NOT NULL,
market_id TEXT NOT NULL,
event_id TEXT NOT NULL,
@ -380,7 +379,7 @@ CREATE TABLE company_odd_settings (
is_active BOOLEAN,
custom_raw_odds JSONB,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(company_id, odds_market_id)
UNIQUE (company_id, odds_market_id)
);
CREATE TABLE result_log (
id BIGSERIAL PRIMARY KEY,
@ -430,7 +429,7 @@ CREATE TABLE company_league_settings (
is_active BOOLEAN,
is_featured BOOLEAN,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(league_id, company_id)
UNIQUE (league_id, company_id)
);
CREATE TABLE teams (
id BIGSERIAL PRIMARY KEY,
@ -447,24 +446,32 @@ CREATE TABLE IF NOT EXISTS global_settings (
);
-- Tenant/Company-specific overrides
CREATE TABLE IF NOT EXISTS company_settings (
company_id BIGINT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
company_id BIGINT NOT NULL REFERENCES companies (id) ON DELETE CASCADE,
key TEXT NOT NULL,
value TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (company_id, key)
);
CREATE TABLE bonus (
multiplier REAL NOT NULL,
CREATE TABLE user_bonuses (
id BIGSERIAL PRIMARY KEY,
balance_cap BIGINT NOT NULL DEFAULT 0
name TEXT NOT NULL,
description TEXT NOT NULL,
type TEXT NOT NULL,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
reward_amount BIGINT NOT NULL,
is_claimed BOOLEAN NOT NULL DEFAULT false,
expires_at TIMESTAMP NOT NULL,
claimed_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE flags (
id BIGSERIAL PRIMARY KEY,
bet_id BIGINT REFERENCES bets(id) ON DELETE CASCADE,
odds_market_id BIGINT REFERENCES odds_market(id),
bet_id BIGINT REFERENCES bets (id) ON DELETE CASCADE,
odds_market_id BIGINT REFERENCES odds_market (id),
reason TEXT,
flagged_at TIMESTAMP DEFAULT NOW(),
flagged_at TIMESTAMP DEFAULT NOW (),
resolved BOOLEAN DEFAULT FALSE,
-- either bet or odd is flagged (not at the same time)
CHECK (
@ -480,21 +487,56 @@ CREATE TABLE flags (
);
CREATE TABLE direct_deposits (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL REFERENCES users(id),
wallet_id BIGINT NOT NULL REFERENCES wallets(id),
customer_id BIGINT NOT NULL REFERENCES users (id),
wallet_id BIGINT NOT NULL REFERENCES wallets (id),
amount NUMERIC(15, 2) NOT NULL,
bank_reference TEXT NOT NULL,
sender_account TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('pending', 'completed', 'rejected')),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
verified_by BIGINT REFERENCES users(id),
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
verified_by BIGINT REFERENCES users (id),
verification_notes TEXT,
verified_at TIMESTAMP
);
CREATE INDEX idx_direct_deposits_status ON direct_deposits(status);
CREATE INDEX idx_direct_deposits_customer ON direct_deposits(customer_id);
CREATE INDEX idx_direct_deposits_reference ON direct_deposits(bank_reference);
-- Views
CREATE INDEX idx_direct_deposits_status ON direct_deposits (status);
CREATE INDEX idx_direct_deposits_customer ON direct_deposits (customer_id);
CREATE INDEX idx_direct_deposits_reference ON direct_deposits (bank_reference);
CREATE TABLE IF NOT EXISTS raffles (
id SERIAL PRIMARY KEY,
company_id INT NOT NULL,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
expires_at TIMESTAMP NOT NULL,
type VARCHAR(50) NOT NULL CHECK (type IN ('virtual', 'sport')),
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'completed'))
);
CREATE TABLE IF NOT EXISTS raffle_tickets (
id SERIAL PRIMARY KEY,
raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE,
user_id INT NOT NULL,
is_active BOOL DEFAULT true
);
CREATE TABLE IF NOT EXISTS raffle_winners (
id SERIAL PRIMARY KEY,
raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE,
user_id INT NOT NULL,
rank INT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS raffle_sport_filters (
id SERIAL PRIMARY KEY,
raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE,
sport_id BIGINT NOT NULL,
league_id BIGINT NOT NULL,
CONSTRAINT unique_raffle_sport_league UNIQUE (raffle_id, sport_id, league_id)
);
CREATE TABLE IF NOT EXISTS raffle_game_filters (
id SERIAL PRIMARY KEY,
raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE,
game_id VARCHAR(150) NOT NULL,
CONSTRAINT unique_raffle_game UNIQUE (raffle_id, game_id)
);
------ Views
CREATE VIEW companies_details AS
SELECT companies.*,
wallets.balance,
@ -508,7 +550,7 @@ FROM companies
;
CREATE VIEW branch_details AS
SELECT branches.*,
CONCAT(users.first_name, ' ', users.last_name) AS manager_name,
CONCAT (users.first_name, ' ', users.last_name) AS manager_name,
users.phone_number AS manager_phone_number,
wallets.balance,
wallets.is_active AS wallet_is_active
@ -522,9 +564,9 @@ CREATE TABLE IF NOT EXISTS supported_operations (
);
CREATE VIEW bet_with_outcomes AS
SELECT bets.*,
CONCAT(users.first_name, ' ', users.last_name) AS full_name,
CONCAT (users.first_name, ' ', users.last_name) AS full_name,
users.phone_number,
JSON_AGG(bet_outcomes.*) AS outcomes
JSON_AGG (bet_outcomes.*) AS outcomes
FROM bets
LEFT JOIN bet_outcomes ON bets.id = bet_outcomes.bet_id
LEFT JOIN users ON bets.user_id = users.id
@ -534,7 +576,7 @@ GROUP BY bets.id,
users.phone_number;
CREATE VIEW ticket_with_outcomes AS
SELECT tickets.*,
JSON_AGG(ticket_outcomes.*) AS outcomes
JSON_AGG (ticket_outcomes.*) AS outcomes
FROM tickets
LEFT JOIN ticket_outcomes ON tickets.id = ticket_outcomes.ticket_id
GROUP BY tickets.id;
@ -588,7 +630,7 @@ SELECT sb.*,
st.verified AS transaction_verified,
bets.status,
bets.total_odds,
JSON_AGG(bet_outcomes.*) AS outcomes
JSON_AGG (bet_outcomes.*) AS outcomes
FROM shop_bets AS sb
JOIN shop_transactions st ON st.id = sb.shop_transaction_id
JOIN bets ON bets.id = sb.bet_id
@ -619,7 +661,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,
@ -632,7 +674,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.*,
@ -654,7 +696,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,
@ -665,47 +707,47 @@ FROM odds_market o
JOIN events e ON o.event_id = e.id;
-- Foreign Keys
ALTER TABLE refresh_tokens
ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id);
ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users (id);
ALTER TABLE bets
ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users(id);
ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users (id);
ALTER TABLE wallets
ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users(id);
ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users (id);
ALTER TABLE customer_wallets
ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users(id),
ADD CONSTRAINT fk_customer_wallets_regular_wallet FOREIGN KEY (regular_wallet_id) REFERENCES wallets(id),
ADD CONSTRAINT fk_customer_wallets_static_wallet FOREIGN KEY (static_wallet_id) REFERENCES wallets(id);
ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users (id),
ADD CONSTRAINT fk_customer_wallets_regular_wallet FOREIGN KEY (regular_wallet_id) REFERENCES wallets (id),
ADD CONSTRAINT fk_customer_wallets_static_wallet FOREIGN KEY (static_wallet_id) REFERENCES wallets (id);
ALTER TABLE wallet_transfer
ADD CONSTRAINT fk_wallet_transfer_receiver_wallet FOREIGN KEY (receiver_wallet_id) REFERENCES wallets(id),
ADD CONSTRAINT fk_wallet_transfer_sender_wallet FOREIGN KEY (sender_wallet_id) REFERENCES wallets(id),
ADD CONSTRAINT fk_wallet_transfer_cashier FOREIGN KEY (cashier_id) REFERENCES users(id);
ADD CONSTRAINT fk_wallet_transfer_receiver_wallet FOREIGN KEY (receiver_wallet_id) REFERENCES wallets (id),
ADD CONSTRAINT fk_wallet_transfer_sender_wallet FOREIGN KEY (sender_wallet_id) REFERENCES wallets (id),
ADD CONSTRAINT fk_wallet_transfer_cashier FOREIGN KEY (cashier_id) REFERENCES users (id);
ALTER TABLE shop_transactions
ADD CONSTRAINT fk_shop_transactions_branches FOREIGN KEY (branch_id) REFERENCES branches(id),
ADD CONSTRAINT fk_shop_transactions_users FOREIGN KEY (user_id) REFERENCES users(id);
ADD CONSTRAINT fk_shop_transactions_branches FOREIGN KEY (branch_id) REFERENCES branches (id),
ADD CONSTRAINT fk_shop_transactions_users FOREIGN KEY (user_id) REFERENCES users (id);
ALTER TABLE shop_bets
ADD CONSTRAINT fk_shop_bet_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions(id),
ADD CONSTRAINT fk_shop_bet_bets FOREIGN KEY (bet_id) REFERENCES bets(id);
ADD CONSTRAINT fk_shop_bet_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions (id),
ADD CONSTRAINT fk_shop_bet_bets FOREIGN KEY (bet_id) REFERENCES bets (id);
ALTER TABLE shop_deposits
ADD CONSTRAINT fk_shop_deposit_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions(id),
ADD CONSTRAINT fk_shop_deposit_customers FOREIGN KEY (customer_id) REFERENCES users(id);
ADD CONSTRAINT fk_shop_deposit_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions (id),
ADD CONSTRAINT fk_shop_deposit_customers FOREIGN KEY (customer_id) REFERENCES users (id);
ALTER TABLE branches
ADD CONSTRAINT fk_branches_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id),
ADD CONSTRAINT fk_branches_manager FOREIGN KEY (branch_manager_id) REFERENCES users(id),
ADD CONSTRAINT fk_branches_location FOREIGN KEY (location) REFERENCES branch_locations(key);
ADD CONSTRAINT fk_branches_wallet FOREIGN KEY (wallet_id) REFERENCES wallets (id),
ADD CONSTRAINT fk_branches_manager FOREIGN KEY (branch_manager_id) REFERENCES users (id),
ADD CONSTRAINT fk_branches_location FOREIGN KEY (location) REFERENCES branch_locations (key);
ALTER TABLE branch_operations
ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERENCES supported_operations(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_branch_operations_branches FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE;
ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERENCES supported_operations (id) ON DELETE CASCADE,
ADD CONSTRAINT fk_branch_operations_branches FOREIGN KEY (branch_id) REFERENCES branches (id) ON DELETE CASCADE;
ALTER TABLE branch_cashiers
ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE;
ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches (id) ON DELETE CASCADE;
ALTER TABLE companies
ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id),
ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE;
ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users (id),
ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets (id) ON DELETE CASCADE;
ALTER TABLE company_league_settings
ADD CONSTRAINT fk_league_settings_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_league_settings_league FOREIGN KEY (league_id) REFERENCES leagues(id) ON DELETE CASCADE;
ADD CONSTRAINT fk_league_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE,
ADD CONSTRAINT fk_league_settings_league FOREIGN KEY (league_id) REFERENCES leagues (id) ON DELETE CASCADE;
ALTER TABLE company_event_settings
ADD CONSTRAINT fk_event_settings_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_event_settings_event FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE;
ADD CONSTRAINT fk_event_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE,
ADD CONSTRAINT fk_event_settings_event FOREIGN KEY (event_id) REFERENCES events (id) ON DELETE CASCADE;
ALTER TABLE company_odd_settings
ADD CONSTRAINT fk_odds_settings_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_odds_settings_odds_market FOREIGN KEY (odds_market_id) REFERENCES odds_market(id) ON DELETE CASCADE;
ADD CONSTRAINT fk_odds_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE,
ADD CONSTRAINT fk_odds_settings_odds_market FOREIGN KEY (odds_market_id) REFERENCES odds_market (id) ON DELETE CASCADE;

View File

@ -18,7 +18,8 @@ CREATE TABLE IF NOT EXISTS notifications (
'admin_alert',
'bet_result',
'transfer_rejected',
'approval_required'
'approval_required',
'bonus_awarded'
)
),
level TEXT NOT NULL CHECK (level IN ('info', 'error', 'warning', 'success')),

View File

@ -1,46 +1,37 @@
CREATE TYPE ReferralStatus AS ENUM ('PENDING', 'COMPLETED', 'EXPIRED', 'CANCELLED');
CREATE TABLE IF NOT EXISTS referral_settings (
id BIGSERIAL PRIMARY KEY,
referral_reward_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
cashback_percentage DECIMAL(5, 2) NOT NULL DEFAULT 0.00,
bet_referral_bonus_percentage NUMERIC DEFAULT 5.0,
max_referrals INTEGER NOT NULL DEFAULT 0,
expires_after_days INTEGER NOT NULL DEFAULT 30,
updated_by VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
version INTEGER NOT NULL DEFAULT 0,
CONSTRAINT referral_reward_amount_positive CHECK (referral_reward_amount >= 0),
CONSTRAINT cashback_percentage_range CHECK (
cashback_percentage >= 0
AND cashback_percentage <= 100
)
);
CREATE TABLE IF NOT EXISTS referrals (
-- CREATE TYPE ReferralStatus AS ENUM ('PENDING', 'COMPLETED', 'EXPIRED', 'CANCELLED');
-- CREATE TABLE IF NOT EXISTS referral_settings (
-- id BIGSERIAL PRIMARY KEY,
-- referral_reward_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
-- cashback_percentage DECIMAL(5, 2) NOT NULL DEFAULT 0.00,
-- bet_referral_bonus_percentage NUMERIC DEFAULT 5.0,
-- max_referrals INTEGER NOT NULL DEFAULT 0,
-- expires_after_days INTEGER NOT NULL DEFAULT 30,
-- updated_by VARCHAR(255) NOT NULL,
-- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- version INTEGER NOT NULL DEFAULT 0,
-- CONSTRAINT referral_reward_amount_positive CHECK (referral_reward_amount >= 0),
-- CONSTRAINT cashback_percentage_range CHECK (
-- cashback_percentage >= 0
-- AND cashback_percentage <= 100
-- )
-- );
CREATE TABLE IF NOT EXISTS referral_codes (
id BIGSERIAL PRIMARY KEY,
referral_code VARCHAR(10) NOT NULL UNIQUE,
referrer_id VARCHAR(255) NOT NULL,
referred_id VARCHAR(255) UNIQUE,
status ReferralStatus NOT NULL DEFAULT 'PENDING',
reward_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
cashback_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
referrer_id BIGINT NOT NULL UNIQUE REFERENCES users (id),
company_id BIGINT NOT NULL REFERENCES companies (id),
is_active BOOLEAN NOT NULL DEFAULT true,
number_of_referrals BIGINT NOT NULL,
reward_amount BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMPTZ NOT NULL,
-- FOREIGN KEY (referrer_id) REFERENCES users (id),
-- FOREIGN KEY (referred_id) REFERENCES users (id),
CONSTRAINT reward_amount_positive CHECK (reward_amount >= 0),
CONSTRAINT cashback_amount_positive CHECK (cashback_amount >= 0)
CONSTRAINT reward_amount_positive CHECK (reward_amount >= 0)
);
CREATE INDEX idx_referrals_referrer_id ON referral_codes (referrer_id);
CREATE TABLE IF NOT EXISTS user_referrals (
id BIGSERIAL PRIMARY KEY,
referred_id BIGINT UNIQUE NOT NULL REFERENCES users (id),
referral_code_id BIGINT NOT NULL REFERENCES referral_codes (id),
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_referrals_referral_code ON referrals (referral_code);
CREATE INDEX idx_referrals_referrer_id ON referrals (referrer_id);
CREATE INDEX idx_referrals_status ON referrals (status);
ALTER TABLE users
ADD COLUMN IF NOT EXISTS referral_code VARCHAR(10) UNIQUE,
ADD COLUMN IF NOT EXISTS referred_by VARCHAR(10);
-- Modify wallet table to track bonus money separately
ALTER TABLE wallets
ADD COLUMN IF NOT EXISTS bonus_balance DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
ADD COLUMN IF NOT EXISTS cash_balance DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
ADD CONSTRAINT bonus_balance_positive CHECK (bonus_balance >= 0),
ADD CONSTRAINT cash_balance_positive CHECK (cash_balance >= 0);

View File

@ -1,11 +1,11 @@
-- Settings Initial Data
INSERT INTO global_settings (key, value)
VALUES ('sms_provider', 'afro_message'),
('max_number_of_outcomes', '30'),
('bet_amount_limit', '10000000'),
('daily_ticket_limit', '50'),
('total_winnings_limit', '1000000'),
('amount_for_bet_referral', '1000000'),
('cashback_amount_cap', '1000') ON CONFLICT (key) DO
UPDATE
SET value = EXCLUDED.value;
-- -- Settings Initial Data
-- INSERT INTO global_settings (key, value)
-- VALUES ('sms_provider', 'afro_message'),
-- ('max_number_of_outcomes', '30'),
-- ('bet_amount_limit', '10000000'),
-- ('daily_ticket_limit', '50'),
-- ('total_winnings_limit', '1000000'),
-- ('amount_for_bet_referral', '1000000'),
-- ('cashback_amount_cap', '1000') ON CONFLICT (key) DO
-- UPDATE
-- SET value = EXCLUDED.value;

View File

@ -1,75 +1,75 @@
-- Locations Initial Data
INSERT INTO branch_locations (key, value)
VALUES ('addis_ababa', 'Addis Ababa'),
('dire_dawa', 'Dire Dawa'),
('mekelle', 'Mekelle'),
('adama', 'Adama'),
('awassa', 'Awassa'),
('bahir_dar', 'Bahir Dar'),
('gonder', 'Gonder'),
('dessie', 'Dessie'),
('jimma', 'Jimma'),
('jijiga', 'Jijiga'),
('shashamane', 'Shashamane'),
('bishoftu', 'Bishoftu'),
('sodo', 'Sodo'),
('arba_minch', 'Arba Minch'),
('hosaena', 'Hosaena'),
('harar', 'Harar'),
('dilla', 'Dilla'),
('nekemte', 'Nekemte'),
('debre_birhan', 'Debre Birhan'),
('asella', 'Asella'),
('debre_markos', 'Debre Markos'),
('kombolcha', 'Kombolcha'),
('debre_tabor', 'Debre Tabor'),
('adigrat', 'Adigrat'),
('areka', 'Areka'),
('weldiya', 'Weldiya'),
('sebeta', 'Sebeta'),
('burayu', 'Burayu'),
('shire', 'Shire'),
('ambo', 'Ambo'),
('arsi_negele', 'Arsi Negele'),
('aksum', 'Aksum'),
('gambela', 'Gambela'),
('bale_robe', 'Bale Robe'),
('butajira', 'Butajira'),
('batu', 'Batu'),
('boditi', 'Boditi'),
('adwa', 'Adwa'),
('yirgalem', 'Yirgalem'),
('waliso', 'Waliso'),
('welkite', 'Welkite'),
('gode', 'Gode'),
('meki', 'Meki'),
('negele_borana', 'Negele Borana'),
('alaba_kulito', 'Alaba Kulito'),
('alamata,', 'Alamata,'),
('chiro', 'Chiro'),
('tepi', 'Tepi'),
('durame', 'Durame'),
('goba', 'Goba'),
('assosa', 'Assosa'),
('gimbi', 'Gimbi'),
('wukro', 'Wukro'),
('haramaya', 'Haramaya'),
('mizan_teferi', 'Mizan Teferi'),
('sawla', 'Sawla'),
('mojo', 'Mojo'),
('dembi_dolo', 'Dembi Dolo'),
('aleta_wendo', 'Aleta Wendo'),
('metu', 'Metu'),
('mota', 'Mota'),
('fiche', 'Fiche'),
('finote_selam', 'Finote Selam'),
('bule_hora_town', 'Bule Hora Town'),
('bonga', 'Bonga'),
('kobo', 'Kobo'),
('jinka', 'Jinka'),
('dangila', 'Dangila'),
('degehabur', 'Degehabur'),
('bedessa', 'Bedessa'),
('agaro', 'Agaro') ON CONFLICT (key) DO
UPDATE
SET value = EXCLUDED.value;
-- -- Locations Initial Data
-- INSERT INTO branch_locations (key, value)
-- VALUES ('addis_ababa', 'Addis Ababa'),
-- ('dire_dawa', 'Dire Dawa'),
-- ('mekelle', 'Mekelle'),
-- ('adama', 'Adama'),
-- ('awassa', 'Awassa'),
-- ('bahir_dar', 'Bahir Dar'),
-- ('gonder', 'Gonder'),
-- ('dessie', 'Dessie'),
-- ('jimma', 'Jimma'),
-- ('jijiga', 'Jijiga'),
-- ('shashamane', 'Shashamane'),
-- ('bishoftu', 'Bishoftu'),
-- ('sodo', 'Sodo'),
-- ('arba_minch', 'Arba Minch'),
-- ('hosaena', 'Hosaena'),
-- ('harar', 'Harar'),
-- ('dilla', 'Dilla'),
-- ('nekemte', 'Nekemte'),
-- ('debre_birhan', 'Debre Birhan'),
-- ('asella', 'Asella'),
-- ('debre_markos', 'Debre Markos'),
-- ('kombolcha', 'Kombolcha'),
-- ('debre_tabor', 'Debre Tabor'),
-- ('adigrat', 'Adigrat'),
-- ('areka', 'Areka'),
-- ('weldiya', 'Weldiya'),
-- ('sebeta', 'Sebeta'),
-- ('burayu', 'Burayu'),
-- ('shire', 'Shire'),
-- ('ambo', 'Ambo'),
-- ('arsi_negele', 'Arsi Negele'),
-- ('aksum', 'Aksum'),
-- ('gambela', 'Gambela'),
-- ('bale_robe', 'Bale Robe'),
-- ('butajira', 'Butajira'),
-- ('batu', 'Batu'),
-- ('boditi', 'Boditi'),
-- ('adwa', 'Adwa'),
-- ('yirgalem', 'Yirgalem'),
-- ('waliso', 'Waliso'),
-- ('welkite', 'Welkite'),
-- ('gode', 'Gode'),
-- ('meki', 'Meki'),
-- ('negele_borana', 'Negele Borana'),
-- ('alaba_kulito', 'Alaba Kulito'),
-- ('alamata,', 'Alamata,'),
-- ('chiro', 'Chiro'),
-- ('tepi', 'Tepi'),
-- ('durame', 'Durame'),
-- ('goba', 'Goba'),
-- ('assosa', 'Assosa'),
-- ('gimbi', 'Gimbi'),
-- ('wukro', 'Wukro'),
-- ('haramaya', 'Haramaya'),
-- ('mizan_teferi', 'Mizan Teferi'),
-- ('sawla', 'Sawla'),
-- ('mojo', 'Mojo'),
-- ('dembi_dolo', 'Dembi Dolo'),
-- ('aleta_wendo', 'Aleta Wendo'),
-- ('metu', 'Metu'),
-- ('mota', 'Mota'),
-- ('fiche', 'Fiche'),
-- ('finote_selam', 'Finote Selam'),
-- ('bule_hora_town', 'Bule Hora Town'),
-- ('bonga', 'Bonga'),
-- ('kobo', 'Kobo'),
-- ('jinka', 'Jinka'),
-- ('dangila', 'Dangila'),
-- ('degehabur', 'Degehabur'),
-- ('bedessa', 'Bedessa'),
-- ('agaro', 'Agaro') ON CONFLICT (key) DO
-- UPDATE
-- SET value = EXCLUDED.value;

View File

@ -1,218 +1,220 @@
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Users
INSERT INTO users (
id,
first_name,
last_name,
email,
phone_number,
password,
role,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
VALUES (
1,
'John',
'Doe',
'john.doe@example.com',
NULL,
crypt('password123', gen_salt('bf'))::bytea,
'customer',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
),
(
2,
'Test',
'Admin',
'test.admin@gmail.com',
'0988554466',
crypt('password123', gen_salt('bf'))::bytea,
'admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
1
),
(
3,
'Samuel',
'Tariku',
'cybersamt@gmail.com',
'0911111111',
crypt('password@123', gen_salt('bf'))::bytea,
'super_admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
),
(
4,
'Kirubel',
'Kibru',
'kirubel.jkl679@gmail.com',
'0911554486',
crypt('password@123', gen_salt('bf'))::bytea,
'super_admin',
TRUE,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
),
(
5,
'Test',
'Veli',
'test.veli@example.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'customer',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
NULL
);
-- Supported Operations
INSERT INTO supported_operations (id, name, description)
VALUES (1, 'SportBook', 'Sportbook operations'),
(2, 'Virtual', 'Virtual operations');
-- Wallets
INSERT INTO wallets (
id,
balance,
is_withdraw,
is_bettable,
is_transferable,
user_id,
type,
currency,
is_active,
created_at,
updated_at
)
VALUES (
1,
10000,
TRUE,
TRUE,
TRUE,
1,
'regular',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
2,
5000,
FALSE,
TRUE,
TRUE,
1,
'static',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
3,
20000,
TRUE,
TRUE,
TRUE,
2,
'company_main',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
),
(
4,
15000,
TRUE,
TRUE,
TRUE,
2,
'branch_main',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
-- Customer Wallets
INSERT INTO customer_wallets (
id,
customer_id,
regular_wallet_id,
static_wallet_id
)
VALUES (1, 1, 1, 2);
-- Company
INSERT INTO companies (
id,
name,
admin_id,
wallet_id,
deducted_percentage,
is_active,
created_at,
updated_at
)
VALUES (
1,
'Test Company',
2,
3,
0.10,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
-- Branch
INSERT INTO branches (
id,
name,
location,
wallet_id,
branch_manager_id,
company_id,
is_self_owned,
profit_percent,
is_active,
created_at,
updated_at
)
VALUES (
1,
'Test Branch',
'addis_ababa',
4,
2,
1,
TRUE,
0.10,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
-- CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- -- Users
-- INSERT INTO users (
-- id,
-- first_name,
-- last_name,
-- email,
-- phone_number,
-- password,
-- role,
-- email_verified,
-- phone_verified,
-- created_at,
-- updated_at,
-- suspended,
-- company_id
-- )
-- VALUES (
-- 1,
-- 'John',
-- 'Doe',
-- 'john.doe@example.com',
-- NULL,
-- crypt('password@123', gen_salt('bf'))::bytea,
-- 'customer',
-- TRUE,
-- FALSE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP,
-- FALSE,
-- 1
-- ),
-- (
-- 2,
-- 'Test',
-- 'Admin',
-- 'test.admin@gmail.com',
-- '0988554466',
-- crypt('password@123', gen_salt('bf'))::bytea,
-- 'admin',
-- TRUE,
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP,
-- FALSE,
-- 1
-- ),
-- (
-- 3,
-- 'Samuel',
-- 'Tariku',
-- 'cybersamt@gmail.com',
-- '0911111111',
-- crypt('password@123', gen_salt('bf'))::bytea,
-- 'super_admin',
-- TRUE,
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP,
-- FALSE,
-- NULL
-- ),
-- (
-- 4,
-- 'Kirubel',
-- 'Kibru',
-- 'kirubel.jkl679@gmail.com',
-- '0911554486',
-- crypt('password@123', gen_salt('bf'))::bytea,
-- 'super_admin',
-- TRUE,
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP,
-- FALSE,
-- NULL
-- ),
-- (
-- 5,
-- 'Test',
-- 'Veli',
-- 'test.veli@example.com',
-- NULL,
-- crypt('password@123', gen_salt('bf'))::bytea,
-- 'customer',
-- TRUE,
-- FALSE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP,
-- FALSE,
-- 1
-- );
-- -- Supported Operations
-- INSERT INTO supported_operations (id, name, description)
-- VALUES (1, 'SportBook', 'Sportbook operations'),
-- (2, 'Virtual', 'Virtual operations');
-- -- Wallets
-- INSERT INTO wallets (
-- id,
-- balance,
-- is_withdraw,
-- is_bettable,
-- is_transferable,
-- user_id,
-- type,
-- currency,
-- is_active,
-- created_at,
-- updated_at
-- )
-- VALUES (
-- 1,
-- 10000,
-- TRUE,
-- TRUE,
-- TRUE,
-- 1,
-- 'regular_wallet',
-- 'ETB',
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP
-- ),
-- (
-- 2,
-- 5000,
-- FALSE,
-- TRUE,
-- TRUE,
-- 1,
-- 'static_wallet',
-- 'ETB',
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP
-- ),
-- (
-- 3,
-- 20000,
-- TRUE,
-- TRUE,
-- TRUE,
-- 2,
-- 'company_wallet',
-- 'ETB',
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP
-- ),
-- (
-- 4,
-- 15000,
-- TRUE,
-- TRUE,
-- TRUE,
-- 2,
-- 'branch_wallet',
-- 'ETB',
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP
-- );
-- -- Customer Wallets
-- INSERT INTO customer_wallets (
-- id,
-- customer_id,
-- regular_wallet_id,
-- static_wallet_id
-- )
-- VALUES (1, 1, 1, 2);
-- -- Company
-- INSERT INTO companies (
-- id,
-- name,
-- slug,
-- admin_id,
-- wallet_id,
-- deducted_percentage,
-- is_active,
-- created_at,
-- updated_at
-- )
-- VALUES (
-- 1,
-- 'FortuneBets',
-- 'fortunebets',
-- 2,
-- 3,
-- 0.10,
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP
-- );
-- -- Branch
-- INSERT INTO branches (
-- id,
-- name,
-- location,
-- wallet_id,
-- branch_manager_id,
-- company_id,
-- is_self_owned,
-- profit_percent,
-- is_active,
-- created_at,
-- updated_at
-- )
-- VALUES (
-- 1,
-- 'Test Branch',
-- 'addis_ababa',
-- 4,
-- 2,
-- 1,
-- TRUE,
-- 0.10,
-- TRUE,
-- CURRENT_TIMESTAMP,
-- CURRENT_TIMESTAMP
-- );

View File

@ -1,17 +1,61 @@
-- name: CreateBonusMultiplier :exec
INSERT INTO bonus (multiplier, balance_cap)
VALUES ($1, $2);
-- name: CreateUserBonus :one
INSERT INTO user_bonuses (
name,
description,
type,
user_id,
reward_amount,
expires_at
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *;
-- name: GetAllUserBonuses :many
SELECT *
FROM user_bonuses
WHERE (
user_id = sqlc.narg('user_id')
OR sqlc.narg('user_id') IS NULL
)
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetUserBonusByID :one
SELECT *
FROM user_bonuses
WHERE id = $1;
-- name: GetBonusMultiplier :many
SELECT id, multiplier
FROM bonus;
-- name: GetBonusBalanceCap :many
SELECT id, balance_cap
FROM bonus;
-- name: UpdateBonusMultiplier :exec
UPDATE bonus
SET multiplier = $1,
balance_cap = $2
WHERE id = $3;
-- name: GetBonusCount :one
SELECT COUNT(*)
FROM user_bonuses
WHERE (
user_id = sqlc.narg('user_id')
OR sqlc.narg('user_id') IS NULL
);
-- name: GetBonusStats :one
SELECT COUNT(*) AS total_bonuses,
COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned,
COUNT(
CASE
WHEN is_claimed = true THEN 1
END
) AS claimed_bonuses,
COUNT(
CASE
WHEN expires_at < now() THEN 1
END
) AS expired_bonuses
FROM user_bonuses
JOIN users ON users.id = user_bonuses.user_id
WHERE (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
user_id = sqlc.narg('user_id')
OR sqlc.narg('user_id') IS NULL
);
-- name: UpdateUserBonus :exec
UPDATE user_bonuses
SET is_claimed = $2
WHERE id = $1;
-- name: DeleteUserBonus :exec
DELETE FROM user_bonuses
WHERE id = $1;

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,8 +54,9 @@ 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: InsertEventSettings :exec
-- name: SaveEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
@ -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,35 @@ 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 e.default_is_featured = sqlc.narg('is_featured')
OR sqlc.narg('is_featured') IS NULL
)
AND (
ces.is_active = sqlc.narg('is_active')
OR e.default_is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') 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 +218,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 +235,19 @@ 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 e.default_is_featured = sqlc.narg('is_featured')
OR sqlc.narg('is_featured') IS NULL
)
AND (
ces.is_active = sqlc.narg('is_active')
OR e.default_is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') IS NULL
)
ORDER BY start_time ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetUpcomingByID :one
@ -223,13 +258,27 @@ 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;
-- name: GetSportAndLeagueIDs :one
SELECT sport_id, league_id FROM events
WHERE id = $1;
-- name: UpdateMatchResult :exec
UPDATE events
SET score = $1,
@ -243,19 +292,6 @@ WHERE id = $1;
UPDATE events
SET is_monitored = $1
WHERE id = $2;
-- name: UpdateEventSettings :exec
UPDATE company_event_settings
SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured = COALESCE(
sqlc.narg('is_featured'),
is_featured
),
winning_upper_limit = COALESCE(
sqlc.narg('winning_upper_limit'),
winning_upper_limit
)
WHERE event_id = $1
AND company_id = $2;
-- name: DeleteEvent :exec
DELETE FROM events
WHERE id = $1;

View File

@ -36,13 +36,18 @@ WHERE (
sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL
)
AND (
name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
ORDER BY name ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetAllLeaguesWithSettings :many
SELECT *
FROM league_with_settings
WHERE (company_id = $1)
AND (
-- name: GetTotalLeaguesWithSettings :one
SELECT COUNT(*)
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
)
@ -52,12 +57,49 @@ WHERE (company_id = $1)
)
AND (
is_active = sqlc.narg('is_active')
OR default_is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') IS NULL
)
AND (
is_featured = sqlc.narg('is_featured')
OR default_is_featured = sqlc.narg('is_featured')
OR sqlc.narg('is_featured') IS NULL
)
AND (
name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
);
-- name: GetAllLeaguesWithSettings :many
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
)
AND (
sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL
)
AND (
is_active = sqlc.narg('is_active')
OR default_is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') IS NULL
)
AND (
is_featured = sqlc.narg('is_featured')
OR default_is_featured = sqlc.narg('is_featured')
OR sqlc.narg('is_featured') IS NULL
)
AND (
name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
ORDER BY is_featured DESC,
name ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');

View File

@ -26,7 +26,7 @@ SET market_type = EXCLUDED.market_type,
raw_odds = EXCLUDED.raw_odds,
fetched_at = EXCLUDED.fetched_at,
expires_at = EXCLUDED.expires_at;
-- name: InsertOddSettings :exec
-- name: SaveOddSettings :exec
INSERT INTO company_odd_settings (
company_id,
odds_market_id,
@ -42,21 +42,69 @@ 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 *
FROM odds_market_with_event
WHERE id = $1;
-- name: GetOddsByMarketID :one
SELECT *
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 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
@ -75,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

73
db/query/raffle.sql Normal file
View File

@ -0,0 +1,73 @@
-- name: CreateRaffle :one
INSERT INTO raffles (company_id, name, expires_at, type)
VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: GetRafflesOfCompany :many
SELECT * FROM raffles WHERE company_id = $1;
-- name: DeleteRaffle :one
DELETE FROM raffles
WHERE id = $1
RETURNING *;
-- name: UpdateRaffleTicketStatus :exec
UPDATE raffle_tickets
SET is_active = $1
WHERE id = $2;
-- name: CreateRaffleTicket :one
INSERT INTO raffle_tickets (raffle_id, user_id)
VALUES ($1, $2)
RETURNING *;
-- name: GetUserRaffleTickets :many
SELECT
rt.id AS ticket_id,
rt.user_id,
r.name,
r.type,
r.expires_at,
r.status
FROM raffle_tickets rt
JOIN raffles r ON rt.raffle_id = r.id
WHERE rt.user_id = $1;
-- name: GetRaffleStanding :many
SELECT
u.id AS user_id,
rt.raffle_id,
u.first_name,
u.last_name,
u.phone_number,
u.email,
COUNT(*) AS ticket_count
FROM raffle_tickets rt
JOIN users u ON rt.user_id = u.id
WHERE rt.is_active = true
AND rt.raffle_id = $1
GROUP BY u.id, rt.raffle_id, u.first_name, u.last_name, u.phone_number, u.email
ORDER BY ticket_count DESC
LIMIT $2;
-- name: CreateRaffleWinner :one
INSERT INTO raffle_winners (raffle_id, user_id, rank)
VALUES ($1, $2, $3)
RETURNING *;
-- name: SetRaffleComplete :exec
UPDATE raffles
SET status = 'completed'
WHERE id = $1;
-- name: AddSportRaffleFilter :one
INSERT INTO raffle_sport_filters (raffle_id, sport_id, league_id)
VALUES ($1, $2, $3)
RETURNING *;
-- name: CheckValidSportRaffleFilter :one
SELECT COUNT(*) > 0 AS exists
FROM raffle_sport_filters
WHERE raffle_id = $1
AND sport_id = $2
AND league_id = $3;

View File

@ -1,77 +1,51 @@
-- name: CreateReferral :one
INSERT INTO referrals (
-- name: CreateReferralCode :one
INSERT INTO referral_codes (
referral_code,
referrer_id,
status,
reward_amount,
expires_at
) VALUES (
$1, $2, $3, $4, $5
) RETURNING *;
-- name: GetReferralByCode :one
SELECT * FROM referrals
WHERE referral_code = $1;
-- name: UpdateReferral :one
UPDATE referrals
SET
referred_id = $2,
status = $3,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
company_id,
number_of_referrals,
reward_amount
)
VALUES ($1, $2, $3, $4, $5)
RETURNING *;
-- name: CreateUserReferral :one
INSERT INTO user_referrals (referred_id, referral_code_id)
VALUES ($1, $2)
RETURNING *;
-- name: GetReferralCodeByUser :many
SELECt *
FROM referral_codes
WHERE referrer_id = $1;
-- name: GetReferralCode :one
SELECT *
FROM referral_codes
WHERE referral_code = $1;
-- name: UpdateReferralCode :exec
UPDATE users
SET
referral_code = $2,
UPDATE referral_codes
SET is_active = $2,
referral_code = $3,
number_of_referrals = $4,
reward_amount = $5,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;
-- name: GetReferralStats :one
SELECT
COUNT(*) as total_referrals,
COUNT(CASE WHEN status = 'COMPLETED' THEN 1 END) as completed_referrals,
COALESCE(SUM(reward_amount), 0) as total_reward_earned,
COALESCE(SUM(CASE WHEN status = 'PENDING' THEN reward_amount END), 0) as pending_rewards
FROM referrals
SELECT COUNT(*) AS total_referrals,
COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned
FROM user_referrals
JOIN referral_codes ON referral_codes.id = referral_code_id
WHERE referrer_id = $1
AND company_id = $2;
-- name: GetUserReferral :one
SELECT *
FROM user_referrals
WHERE referred_id = $1;
-- name: GetUserReferralsByCode :many
SELECT user_referrals.*
FROM user_referrals
JOIN referral_codes ON referral_codes.id = referral_code_id
WHERE referral_code = $1;
-- name: GetUserReferralsCount :one
SELECT COUNT(*)
FROM user_referrals
JOIN referral_codes ON referral_codes.id = referral_code_id
WHERE referrer_id = $1;
-- name: GetReferralSettings :one
SELECT * FROM referral_settings
LIMIT 1;
-- name: UpdateReferralSettings :one
UPDATE referral_settings
SET
referral_reward_amount = $2,
cashback_percentage = $3,
bet_referral_bonus_percentage= $4,
max_referrals = $5,
expires_after_days = $6,
updated_by = $7,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING *;
-- name: CreateReferralSettings :one
INSERT INTO referral_settings (
referral_reward_amount,
cashback_percentage,
max_referrals,
bet_referral_bonus_percentage,
expires_after_days,
updated_by
) VALUES (
$1, $2, $3, $4, $5, $6
) RETURNING *;
-- name: GetReferralByReferredID :one
SELECT * FROM referrals WHERE referred_id = $1 LIMIT 1;
-- name: GetActiveReferralByReferrerID :one
SELECT * FROM referrals WHERE referrer_id = $1 AND status = 'PENDING' LIMIT 1;
-- name: GetReferralCountByID :one
SELECT count(*) FROM referrals WHERE referrer_id = $1;

View File

@ -27,7 +27,9 @@ SELECT *
FROM company_settings
WHERE key = $1;
-- name: GetOverrideSettings :many
SELECT gs.*,
SELECT gs.key,
gs.created_at,
gs.updated_at,
COALESCE(cs.value, gs.value) AS value
FROM global_settings gs
LEFT JOIN company_settings cs ON cs.key = gs.key

View File

@ -30,6 +30,19 @@ WHERE id = $1;
SELECT *
FROM wallet_transfer_details
WHERE reference_number = $1;
-- name: GetTransferStats :one
SELECT COUNT(*) AS total_transfers, COUNT(*) FILTER (
WHERE type = 'deposit'
) AS total_deposits,
COUNT(*) FILTER (
WHERE type = 'withdraw'
) AS total_withdraw,
COUNT(*) FILTER (
WHERE type = 'wallet'
) AS total_wallet_to_wallet
FROM wallet_transfer
WHERE sender_wallet_id = $1
OR receiver_wallet_id = $1;
-- name: UpdateTransferVerification :exec
UPDATE wallet_transfer
SET verified = $1,

View File

@ -193,7 +193,7 @@ SET password = $1,
WHERE (
email = $2
OR phone_number = $3
AND company_id = $4
AND company_id = $5
);
-- name: GetAdminByCompanyID :one
SELECT users.*

View File

@ -1,59 +1,127 @@
-- name: CreateVirtualGameProvider :one
INSERT INTO virtual_game_providers (
provider_id, provider_name, logo_dark, logo_light, enabled
) VALUES (
$1, $2, $3, $4, $5
) RETURNING id, provider_id, provider_name, logo_dark, logo_light, enabled, created_at, updated_at;
provider_id,
provider_name,
logo_dark,
logo_light,
enabled
)
VALUES ($1, $2, $3, $4, $5)
RETURNING id,
provider_id,
provider_name,
logo_dark,
logo_light,
enabled,
created_at,
updated_at;
-- name: DeleteVirtualGameProvider :exec
DELETE FROM virtual_game_providers
WHERE provider_id = $1;
-- name: DeleteAllVirtualGameProviders :exec
DELETE FROM virtual_game_providers;
-- name: GetVirtualGameProviderByID :one
SELECT id, provider_id, provider_name, logo_dark, logo_light, enabled, created_at, updated_at
SELECT id,
provider_id,
provider_name,
logo_dark,
logo_light,
enabled,
created_at,
updated_at
FROM virtual_game_providers
WHERE provider_id = $1;
-- name: ListVirtualGameProviders :many
SELECT id, provider_id, provider_name, logo_dark, logo_light, enabled, created_at, updated_at
SELECT id,
provider_id,
provider_name,
logo_dark,
logo_light,
enabled,
created_at,
updated_at
FROM virtual_game_providers
ORDER BY created_at DESC
LIMIT $1 OFFSET $2;
-- name: CountVirtualGameProviders :one
SELECT COUNT(*) AS total
FROM virtual_game_providers;
-- name: UpdateVirtualGameProviderEnabled :one
UPDATE virtual_game_providers
SET enabled = $2,
updated_at = CURRENT_TIMESTAMP
WHERE provider_id = $1
RETURNING id, provider_id, provider_name, logo_dark, logo_light, enabled, created_at, updated_at;
RETURNING id,
provider_id,
provider_name,
logo_dark,
logo_light,
enabled,
created_at,
updated_at;
-- name: CreateVirtualGameSession :one
INSERT INTO virtual_game_sessions (
user_id, game_id, session_token, currency, status, expires_at
) VALUES (
$1, $2, $3, $4, $5, $6
) RETURNING id, user_id, game_id, session_token, currency, status, created_at, updated_at, expires_at;
user_id,
game_id,
session_token,
currency,
status,
expires_at
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id,
user_id,
game_id,
session_token,
currency,
status,
created_at,
updated_at,
expires_at;
-- name: GetVirtualGameSessionByToken :one
SELECT id, user_id, game_id, session_token, currency, status, created_at, updated_at, expires_at
SELECT id,
user_id,
game_id,
session_token,
currency,
status,
created_at,
updated_at,
expires_at
FROM virtual_game_sessions
WHERE session_token = $1;
-- name: UpdateVirtualGameSessionStatus :exec
UPDATE virtual_game_sessions
SET status = $2, updated_at = CURRENT_TIMESTAMP
SET status = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;
-- name: CreateVirtualGameTransaction :one
INSERT INTO virtual_game_transactions (
session_id, user_id, company_id, provider, wallet_id, transaction_type, amount, currency, external_transaction_id, status
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
) RETURNING id, session_id, user_id, company_id, provider, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at;
session_id,
user_id,
company_id,
provider,
wallet_id,
transaction_type,
amount,
currency,
external_transaction_id,
status
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id,
session_id,
user_id,
company_id,
provider,
wallet_id,
transaction_type,
amount,
currency,
external_transaction_id,
status,
created_at,
updated_at;
-- name: CreateVirtualGameHistory :one
INSERT INTO virtual_game_histories (
session_id,
@ -68,10 +136,22 @@ INSERT INTO virtual_game_histories (
external_transaction_id,
reference_transaction_id,
status
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
) RETURNING
id,
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12
)
RETURNING id,
session_id,
user_id,
company_id,
@ -87,41 +167,48 @@ INSERT INTO virtual_game_histories (
created_at,
updated_at;
-- name: GetVirtualGameTransactionByExternalID :one
SELECT id, session_id, user_id, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at
SELECT id,
session_id,
user_id,
wallet_id,
transaction_type,
amount,
currency,
external_transaction_id,
status,
created_at,
updated_at
FROM virtual_game_transactions
WHERE external_transaction_id = $1;
-- name: UpdateVirtualGameTransactionStatus :exec
UPDATE virtual_game_transactions
SET status = $2, updated_at = CURRENT_TIMESTAMP
SET status = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;
-- name: GetVirtualGameSummaryInRange :many
SELECT
c.name AS company_name,
SELECT c.name AS company_name,
vg.name AS game_name,
COUNT(vgt.id) AS number_of_bets,
COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum
FROM virtual_game_transactions vgt
JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
JOIN virtual_games vg ON vgs.game_id = vg.id
JOIN companies c ON vgt.company_id = c.id
JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
JOIN virtual_games vg ON vgs.game_id = vg.id
JOIN companies c ON vgt.company_id = c.id
WHERE vgt.transaction_type = 'BET'
AND vgt.created_at BETWEEN $1 AND $2
GROUP BY c.name, vg.name;
GROUP BY c.name,
vg.name;
-- name: AddFavoriteGame :exec
INSERT INTO favorite_games (
user_id,
game_id,
created_at
) VALUES ($1, $2, NOW())
ON CONFLICT (user_id, game_id) DO NOTHING;
INSERT INTO favorite_games (user_id, game_id, created_at)
VALUES ($1, $2, NOW()) ON CONFLICT (user_id, game_id) DO NOTHING;
-- name: RemoveFavoriteGame :exec
DELETE FROM favorite_games
WHERE user_id = $1 AND game_id = $2;
WHERE user_id = $1
AND game_id = $2;
-- name: ListFavoriteGames :many
SELECT game_id
FROM favorite_games
WHERE user_id = $1;
-- name: CreateVirtualGame :one
INSERT INTO virtual_games (
game_id,
@ -136,11 +223,22 @@ INSERT INTO virtual_games (
bets,
thumbnail,
status
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
)
RETURNING
id,
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12
)
RETURNING id,
game_id,
provider_id,
name,
@ -155,10 +253,8 @@ RETURNING
status,
created_at,
updated_at;
-- name: GetAllVirtualGames :many
SELECT
vg.id,
SELECT vg.id,
vg.game_id,
vg.provider_id,
vp.provider_name,
@ -175,14 +271,20 @@ SELECT
vg.created_at,
vg.updated_at
FROM virtual_games vg
JOIN virtual_game_providers vp ON vg.provider_id = vp.provider_id
WHERE
($1::text IS NULL OR vg.category = $1) -- category filter (optional)
AND ($2::text IS NULL OR vg.name ILIKE '%' || $2 || '%') -- search by name (optional)
JOIN virtual_game_providers vp ON vg.provider_id = vp.provider_id
WHERE (
vg.category = sqlc.narg('category')
OR sqlc.narg('category') IS NULL
)
AND (
name ILIKE '%' || sqlc.narg('name') || '%'
OR sqlc.narg('name') IS NULL
)
AND (
vg.provider_id = sqlc.narg('provider_id')
OR sqlc.narg('provider_id') IS NULL
)
ORDER BY vg.created_at DESC
LIMIT $3 OFFSET $4;
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: DeleteAllVirtualGames :exec
DELETE FROM virtual_games;

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: auth.sql
package dbgen
@ -76,7 +76,7 @@ func (q *Queries) GetRefreshTokenByUserID(ctx context.Context, userID int64) (Re
}
const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended
FROM users
WHERE (
email = $1
@ -112,8 +112,6 @@ func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPho
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,
&i.ReferredBy,
)
return i, err
}

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: bet.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: bet_stat.sql
package dbgen

View File

@ -1,49 +1,112 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: bonus.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateBonusMultiplier = `-- name: CreateBonusMultiplier :exec
INSERT INTO bonus (multiplier, balance_cap)
VALUES ($1, $2)
const CreateUserBonus = `-- name: CreateUserBonus :one
INSERT INTO user_bonuses (
name,
description,
type,
user_id,
reward_amount,
expires_at
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, name, description, type, user_id, reward_amount, is_claimed, expires_at, claimed_at, created_at, updated_at
`
type CreateBonusMultiplierParams struct {
Multiplier float32 `json:"multiplier"`
BalanceCap int64 `json:"balance_cap"`
type CreateUserBonusParams struct {
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
UserID int64 `json:"user_id"`
RewardAmount int64 `json:"reward_amount"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
}
func (q *Queries) CreateBonusMultiplier(ctx context.Context, arg CreateBonusMultiplierParams) error {
_, err := q.db.Exec(ctx, CreateBonusMultiplier, arg.Multiplier, arg.BalanceCap)
func (q *Queries) CreateUserBonus(ctx context.Context, arg CreateUserBonusParams) (UserBonuse, error) {
row := q.db.QueryRow(ctx, CreateUserBonus,
arg.Name,
arg.Description,
arg.Type,
arg.UserID,
arg.RewardAmount,
arg.ExpiresAt,
)
var i UserBonuse
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Type,
&i.UserID,
&i.RewardAmount,
&i.IsClaimed,
&i.ExpiresAt,
&i.ClaimedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const DeleteUserBonus = `-- name: DeleteUserBonus :exec
DELETE FROM user_bonuses
WHERE id = $1
`
func (q *Queries) DeleteUserBonus(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteUserBonus, id)
return err
}
const GetBonusBalanceCap = `-- name: GetBonusBalanceCap :many
SELECT id, balance_cap
FROM bonus
const GetAllUserBonuses = `-- name: GetAllUserBonuses :many
SELECT id, name, description, type, user_id, reward_amount, is_claimed, expires_at, claimed_at, created_at, updated_at
FROM user_bonuses
WHERE (
user_id = $1
OR $1 IS NULL
)
LIMIT $3 OFFSET $2
`
type GetBonusBalanceCapRow struct {
ID int64 `json:"id"`
BalanceCap int64 `json:"balance_cap"`
type GetAllUserBonusesParams struct {
UserID pgtype.Int8 `json:"user_id"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
func (q *Queries) GetBonusBalanceCap(ctx context.Context) ([]GetBonusBalanceCapRow, error) {
rows, err := q.db.Query(ctx, GetBonusBalanceCap)
func (q *Queries) GetAllUserBonuses(ctx context.Context, arg GetAllUserBonusesParams) ([]UserBonuse, error) {
rows, err := q.db.Query(ctx, GetAllUserBonuses, arg.UserID, arg.Offset, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetBonusBalanceCapRow
var items []UserBonuse
for rows.Next() {
var i GetBonusBalanceCapRow
if err := rows.Scan(&i.ID, &i.BalanceCap); err != nil {
var i UserBonuse
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Type,
&i.UserID,
&i.RewardAmount,
&i.IsClaimed,
&i.ExpiresAt,
&i.ClaimedAt,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
@ -54,50 +117,108 @@ func (q *Queries) GetBonusBalanceCap(ctx context.Context) ([]GetBonusBalanceCapR
return items, nil
}
const GetBonusMultiplier = `-- name: GetBonusMultiplier :many
SELECT id, multiplier
FROM bonus
const GetBonusCount = `-- name: GetBonusCount :one
SELECT COUNT(*)
FROM user_bonuses
WHERE (
user_id = $1
OR $1 IS NULL
)
`
type GetBonusMultiplierRow struct {
ID int64 `json:"id"`
Multiplier float32 `json:"multiplier"`
func (q *Queries) GetBonusCount(ctx context.Context, userID pgtype.Int8) (int64, error) {
row := q.db.QueryRow(ctx, GetBonusCount, userID)
var count int64
err := row.Scan(&count)
return count, err
}
func (q *Queries) GetBonusMultiplier(ctx context.Context) ([]GetBonusMultiplierRow, error) {
rows, err := q.db.Query(ctx, GetBonusMultiplier)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetBonusMultiplierRow
for rows.Next() {
var i GetBonusMultiplierRow
if err := rows.Scan(&i.ID, &i.Multiplier); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateBonusMultiplier = `-- name: UpdateBonusMultiplier :exec
UPDATE bonus
SET multiplier = $1,
balance_cap = $2
WHERE id = $3
const GetBonusStats = `-- name: GetBonusStats :one
SELECT COUNT(*) AS total_bonuses,
COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned,
COUNT(
CASE
WHEN is_claimed = true THEN 1
END
) AS claimed_bonuses,
COUNT(
CASE
WHEN expires_at < now() THEN 1
END
) AS expired_bonuses
FROM user_bonuses
JOIN users ON users.id = user_bonuses.user_id
WHERE (
company_id = $1
OR $1 IS NULL
)
AND (
user_id = $2
OR $2 IS NULL
)
`
type UpdateBonusMultiplierParams struct {
Multiplier float32 `json:"multiplier"`
BalanceCap int64 `json:"balance_cap"`
ID int64 `json:"id"`
type GetBonusStatsParams struct {
CompanyID pgtype.Int8 `json:"company_id"`
UserID pgtype.Int8 `json:"user_id"`
}
func (q *Queries) UpdateBonusMultiplier(ctx context.Context, arg UpdateBonusMultiplierParams) error {
_, err := q.db.Exec(ctx, UpdateBonusMultiplier, arg.Multiplier, arg.BalanceCap, arg.ID)
type GetBonusStatsRow struct {
TotalBonuses int64 `json:"total_bonuses"`
TotalRewardEarned int64 `json:"total_reward_earned"`
ClaimedBonuses int64 `json:"claimed_bonuses"`
ExpiredBonuses int64 `json:"expired_bonuses"`
}
func (q *Queries) GetBonusStats(ctx context.Context, arg GetBonusStatsParams) (GetBonusStatsRow, error) {
row := q.db.QueryRow(ctx, GetBonusStats, arg.CompanyID, arg.UserID)
var i GetBonusStatsRow
err := row.Scan(
&i.TotalBonuses,
&i.TotalRewardEarned,
&i.ClaimedBonuses,
&i.ExpiredBonuses,
)
return i, err
}
const GetUserBonusByID = `-- name: GetUserBonusByID :one
SELECT id, name, description, type, user_id, reward_amount, is_claimed, expires_at, claimed_at, created_at, updated_at
FROM user_bonuses
WHERE id = $1
`
func (q *Queries) GetUserBonusByID(ctx context.Context, id int64) (UserBonuse, error) {
row := q.db.QueryRow(ctx, GetUserBonusByID, id)
var i UserBonuse
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Type,
&i.UserID,
&i.RewardAmount,
&i.IsClaimed,
&i.ExpiresAt,
&i.ClaimedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const UpdateUserBonus = `-- name: UpdateUserBonus :exec
UPDATE user_bonuses
SET is_claimed = $2
WHERE id = $1
`
type UpdateUserBonusParams struct {
ID int64 `json:"id"`
IsClaimed bool `json:"is_claimed"`
}
func (q *Queries) UpdateUserBonus(ctx context.Context, arg UpdateUserBonusParams) error {
_, err := q.db.Exec(ctx, UpdateUserBonus, arg.ID, arg.IsClaimed)
return err
}

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: branch.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: cashier.sql
package dbgen
@ -12,7 +12,7 @@ import (
)
const GetAllCashiers = `-- name: GetAllCashiers :many
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by,
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended,
branch_id,
branches.name AS branch_name,
branches.wallet_id AS branch_wallet,
@ -57,8 +57,6 @@ type GetAllCashiersRow struct {
CompanyID pgtype.Int8 `json:"company_id"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
Suspended bool `json:"suspended"`
ReferralCode pgtype.Text `json:"referral_code"`
ReferredBy pgtype.Text `json:"referred_by"`
BranchID int64 `json:"branch_id"`
BranchName string `json:"branch_name"`
BranchWallet int64 `json:"branch_wallet"`
@ -89,8 +87,6 @@ func (q *Queries) GetAllCashiers(ctx context.Context, arg GetAllCashiersParams)
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,
&i.ReferredBy,
&i.BranchID,
&i.BranchName,
&i.BranchWallet,
@ -107,7 +103,7 @@ func (q *Queries) GetAllCashiers(ctx context.Context, arg GetAllCashiersParams)
}
const GetCashierByID = `-- name: GetCashierByID :one
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by,
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended,
branch_id,
branches.name AS branch_name,
branches.wallet_id AS branch_wallet,
@ -133,8 +129,6 @@ type GetCashierByIDRow struct {
CompanyID pgtype.Int8 `json:"company_id"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
Suspended bool `json:"suspended"`
ReferralCode pgtype.Text `json:"referral_code"`
ReferredBy pgtype.Text `json:"referred_by"`
BranchID int64 `json:"branch_id"`
BranchName string `json:"branch_name"`
BranchWallet int64 `json:"branch_wallet"`
@ -159,8 +153,6 @@ func (q *Queries) GetCashierByID(ctx context.Context, id int64) (GetCashierByIDR
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,
&i.ReferredBy,
&i.BranchID,
&i.BranchName,
&i.BranchWallet,
@ -170,7 +162,7 @@ func (q *Queries) GetCashierByID(ctx context.Context, id int64) (GetCashierByIDR
}
const GetCashiersByBranch = `-- name: GetCashiersByBranch :many
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended
FROM branch_cashiers
JOIN users ON branch_cashiers.user_id = users.id
JOIN branches ON branches.id = branch_id
@ -201,8 +193,6 @@ func (q *Queries) GetCashiersByBranch(ctx context.Context, branchID int64) ([]Us
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,
&i.ReferredBy,
); err != nil {
return nil, err
}

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: company.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: copyfrom.go
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: direct_deposit.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: disabled_odds.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: event_history.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: events.sql
package dbgen
@ -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,21 @@ 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 e.default_is_featured = $8
OR $8 IS NULL
)
AND (
ces.is_active = $9
OR e.default_is_active = $9
OR $9 IS NULL
)
ORDER BY start_time ASC
LIMIT $9 OFFSET $8
LIMIT $11 OFFSET $10
`
type GetEventsWithSettingsParams struct {
@ -175,11 +241,47 @@ 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"`
IsActive pgtype.Bool `json:"is_active"`
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 +290,8 @@ func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSe
arg.LastStartTime,
arg.FirstStartTime,
arg.CountryCode,
arg.IsFeatured,
arg.IsActive,
arg.Offset,
arg.Limit,
)
@ -195,9 +299,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,
@ -401,18 +505,37 @@ func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginat
return items, nil
}
const GetSportAndLeagueIDs = `-- name: GetSportAndLeagueIDs :one
SELECT sport_id, league_id FROM events
WHERE id = $1
`
type GetSportAndLeagueIDsRow struct {
SportID int32 `json:"sport_id"`
LeagueID int64 `json:"league_id"`
}
func (q *Queries) GetSportAndLeagueIDs(ctx context.Context, id string) (GetSportAndLeagueIDsRow, error) {
row := q.db.QueryRow(ctx, GetSportAndLeagueIDs, id)
var i GetSportAndLeagueIDsRow
err := row.Scan(&i.SportID, &i.LeagueID)
return i, err
}
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 +552,19 @@ 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 e.default_is_featured = $8
OR $8 IS NULL
)
AND (
ces.is_active = $9
OR e.default_is_active = $9
OR $9 IS NULL
)
`
type GetTotalCompanyEventsParams struct {
@ -442,6 +575,8 @@ 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"`
IsActive pgtype.Bool `json:"is_active"`
}
func (q *Queries) GetTotalCompanyEvents(ctx context.Context, arg GetTotalCompanyEventsParams) (int64, error) {
@ -453,6 +588,8 @@ func (q *Queries) GetTotalCompanyEvents(ctx context.Context, arg GetTotalCompany
arg.LastStartTime,
arg.FirstStartTime,
arg.CountryCode,
arg.IsFeatured,
arg.IsActive,
)
var count int64
err := row.Scan(&count)
@ -573,7 +710,8 @@ INSERT INTO events (
start_time,
is_live,
status,
source
source,
default_winning_upper_limit
)
VALUES (
$1,
@ -590,7 +728,8 @@ VALUES (
$12,
$13,
$14,
$15
$15,
$16
) ON CONFLICT (id) DO
UPDATE
SET sport_id = EXCLUDED.sport_id,
@ -603,7 +742,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 +750,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 +770,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,40 +790,7 @@ func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error
arg.IsLive,
arg.Status,
arg.Source,
)
return err
}
const InsertEventSettings = `-- name: InsertEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
is_active,
is_featured,
winning_upper_limit
)
VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured,
winning_upper_limit = EXCLUDED.winning_upper_limit
`
type InsertEventSettingsParams struct {
CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
}
func (q *Queries) InsertEventSettings(ctx context.Context, arg InsertEventSettingsParams) error {
_, err := q.db.Exec(ctx, InsertEventSettings,
arg.CompanyID,
arg.EventID,
arg.IsActive,
arg.IsFeatured,
arg.WinningUpperLimit,
arg.DefaultWinningUpperLimit,
)
return err
}
@ -727,6 +834,40 @@ func (q *Queries) ListLiveEvents(ctx context.Context) ([]string, error) {
return items, nil
}
const SaveEventSettings = `-- name: SaveEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
is_active,
is_featured,
winning_upper_limit
)
VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured,
winning_upper_limit = EXCLUDED.winning_upper_limit
`
type SaveEventSettingsParams struct {
CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
}
func (q *Queries) SaveEventSettings(ctx context.Context, arg SaveEventSettingsParams) error {
_, err := q.db.Exec(ctx, SaveEventSettings,
arg.CompanyID,
arg.EventID,
arg.IsActive,
arg.IsFeatured,
arg.WinningUpperLimit,
)
return err
}
const UpdateEventMonitored = `-- name: UpdateEventMonitored :exec
UPDATE events
SET is_monitored = $1
@ -743,40 +884,6 @@ func (q *Queries) UpdateEventMonitored(ctx context.Context, arg UpdateEventMonit
return err
}
const UpdateEventSettings = `-- name: UpdateEventSettings :exec
UPDATE company_event_settings
SET is_active = COALESCE($3, is_active),
is_featured = COALESCE(
$4,
is_featured
),
winning_upper_limit = COALESCE(
$5,
winning_upper_limit
)
WHERE event_id = $1
AND company_id = $2
`
type UpdateEventSettingsParams struct {
EventID string `json:"event_id"`
CompanyID int64 `json:"company_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
}
func (q *Queries) UpdateEventSettings(ctx context.Context, arg UpdateEventSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateEventSettings,
arg.EventID,
arg.CompanyID,
arg.IsActive,
arg.IsFeatured,
arg.WinningUpperLimit,
)
return err
}
const UpdateMatchResult = `-- name: UpdateMatchResult :exec
UPDATE events
SET score = $1,

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: events_stat.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: flags.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: institutions.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: issue_reporting.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: leagues.sql
package dbgen
@ -44,13 +44,18 @@ WHERE (
sport_id = $2
OR $2 IS NULL
)
AND (
name ILIKE '%' || $3 || '%'
OR $3 IS NULL
)
ORDER BY name ASC
LIMIT $4 OFFSET $3
LIMIT $5 OFFSET $4
`
type GetAllLeaguesParams struct {
CountryCode pgtype.Text `json:"country_code"`
SportID pgtype.Int4 `json:"sport_id"`
Query pgtype.Text `json:"query"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
@ -59,6 +64,7 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
rows, err := q.db.Query(ctx, GetAllLeagues,
arg.CountryCode,
arg.SportID,
arg.Query,
arg.Offset,
arg.Limit,
)
@ -90,10 +96,15 @@ 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
)
@ -103,15 +114,21 @@ WHERE (company_id = $1)
)
AND (
is_active = $4
OR default_is_active = $4
OR $4 IS NULL
)
AND (
is_featured = $5
OR default_is_featured = $5
OR $5 IS NULL
)
AND (
name ILIKE '%' || $6 || '%'
OR $6 IS NULL
)
ORDER BY is_featured DESC,
name ASC
LIMIT $7 OFFSET $6
LIMIT $8 OFFSET $7
`
type GetAllLeaguesWithSettingsParams struct {
@ -120,17 +137,34 @@ type GetAllLeaguesWithSettingsParams struct {
SportID pgtype.Int4 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
Query pgtype.Text `json:"query"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
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,
arg.SportID,
arg.IsActive,
arg.IsFeatured,
arg.Query,
arg.Offset,
arg.Limit,
)
@ -138,9 +172,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,
@ -165,6 +199,58 @@ func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeagu
return items, nil
}
const GetTotalLeaguesWithSettings = `-- name: GetTotalLeaguesWithSettings :one
SELECT COUNT(*)
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
)
AND (
sport_id = $3
OR $3 IS NULL
)
AND (
is_active = $4
OR default_is_active = $4
OR $4 IS NULL
)
AND (
is_featured = $5
OR default_is_featured = $5
OR $5 IS NULL
)
AND (
name ILIKE '%' || $6 || '%'
OR $6 IS NULL
)
`
type GetTotalLeaguesWithSettingsParams struct {
CompanyID int64 `json:"company_id"`
CountryCode pgtype.Text `json:"country_code"`
SportID pgtype.Int4 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
Query pgtype.Text `json:"query"`
}
func (q *Queries) GetTotalLeaguesWithSettings(ctx context.Context, arg GetTotalLeaguesWithSettingsParams) (int64, error) {
row := q.db.QueryRow(ctx, GetTotalLeaguesWithSettings,
arg.CompanyID,
arg.CountryCode,
arg.SportID,
arg.IsActive,
arg.IsFeatured,
arg.Query,
)
var count int64
err := row.Scan(&count)
return count, err
}
const InsertLeague = `-- name: InsertLeague :exec
INSERT INTO leagues (
id,

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: location.sql
package dbgen

View File

@ -1,60 +1,13 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
package dbgen
import (
"database/sql/driver"
"fmt"
"github.com/jackc/pgx/v5/pgtype"
)
type Referralstatus string
const (
ReferralstatusPENDING Referralstatus = "PENDING"
ReferralstatusCOMPLETED Referralstatus = "COMPLETED"
ReferralstatusEXPIRED Referralstatus = "EXPIRED"
ReferralstatusCANCELLED Referralstatus = "CANCELLED"
)
func (e *Referralstatus) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = Referralstatus(s)
case string:
*e = Referralstatus(s)
default:
return fmt.Errorf("unsupported scan type for Referralstatus: %T", src)
}
return nil
}
type NullReferralstatus struct {
Referralstatus Referralstatus `json:"referralstatus"`
Valid bool `json:"valid"` // Valid is true if Referralstatus is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullReferralstatus) Scan(value interface{}) error {
if value == nil {
ns.Referralstatus, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.Referralstatus.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullReferralstatus) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.Referralstatus), nil
}
type Bank struct {
ID int64 `json:"id"`
Slug string `json:"slug"`
@ -126,12 +79,6 @@ type BetWithOutcome struct {
Outcomes []BetOutcome `json:"outcomes"`
}
type Bonu struct {
Multiplier float32 `json:"multiplier"`
ID int64 `json:"id"`
BalanceCap int64 `json:"balance_cap"`
}
type Branch struct {
ID int64 `json:"id"`
Name string `json:"name"`
@ -321,7 +268,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 +303,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 +332,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 +394,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 +467,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"`
@ -538,30 +485,54 @@ type Otp struct {
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
}
type Referral struct {
ID int64 `json:"id"`
ReferralCode string `json:"referral_code"`
ReferrerID string `json:"referrer_id"`
ReferredID pgtype.Text `json:"referred_id"`
Status Referralstatus `json:"status"`
RewardAmount pgtype.Numeric `json:"reward_amount"`
CashbackAmount pgtype.Numeric `json:"cashback_amount"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
type Raffle struct {
ID int32 `json:"id"`
CompanyID int32 `json:"company_id"`
Name string `json:"name"`
CreatedAt pgtype.Timestamp `json:"created_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
Type string `json:"type"`
Status string `json:"status"`
}
type ReferralSetting struct {
type RaffleGameFilter struct {
ID int32 `json:"id"`
RaffleID int32 `json:"raffle_id"`
GameID string `json:"game_id"`
}
type RaffleSportFilter struct {
ID int32 `json:"id"`
RaffleID int32 `json:"raffle_id"`
SportID int64 `json:"sport_id"`
LeagueID int64 `json:"league_id"`
}
type RaffleTicket struct {
ID int32 `json:"id"`
RaffleID int32 `json:"raffle_id"`
UserID int32 `json:"user_id"`
IsActive pgtype.Bool `json:"is_active"`
}
type RaffleWinner struct {
ID int32 `json:"id"`
RaffleID int32 `json:"raffle_id"`
UserID int32 `json:"user_id"`
Rank int32 `json:"rank"`
CreatedAt pgtype.Timestamp `json:"created_at"`
}
type ReferralCode struct {
ID int64 `json:"id"`
ReferralRewardAmount pgtype.Numeric `json:"referral_reward_amount"`
CashbackPercentage pgtype.Numeric `json:"cashback_percentage"`
BetReferralBonusPercentage pgtype.Numeric `json:"bet_referral_bonus_percentage"`
MaxReferrals int32 `json:"max_referrals"`
ExpiresAfterDays int32 `json:"expires_after_days"`
UpdatedBy string `json:"updated_by"`
ReferralCode string `json:"referral_code"`
ReferrerID int64 `json:"referrer_id"`
CompanyID int64 `json:"company_id"`
IsActive bool `json:"is_active"`
NumberOfReferrals int64 `json:"number_of_referrals"`
RewardAmount int64 `json:"reward_amount"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Version int32 `json:"version"`
}
type RefreshToken struct {
@ -794,8 +765,20 @@ type User struct {
CompanyID pgtype.Int8 `json:"company_id"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
Suspended bool `json:"suspended"`
ReferralCode pgtype.Text `json:"referral_code"`
ReferredBy pgtype.Text `json:"referred_by"`
}
type UserBonuse struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
UserID int64 `json:"user_id"`
RewardAmount int64 `json:"reward_amount"`
IsClaimed bool `json:"is_claimed"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
ClaimedAt pgtype.Timestamp `json:"claimed_at"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type UserGameInteraction struct {
@ -808,6 +791,13 @@ type UserGameInteraction struct {
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type UserReferral struct {
ID int64 `json:"id"`
ReferredID int64 `json:"referred_id"`
ReferralCodeID int64 `json:"referral_code_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type VirtualGame struct {
ID int64 `json:"id"`
GameID string `json:"game_id"`
@ -896,8 +886,6 @@ type Wallet struct {
IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
BonusBalance pgtype.Numeric `json:"bonus_balance"`
CashBalance pgtype.Numeric `json:"cash_balance"`
}
type WalletThresholdNotification struct {

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: monitor.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: notification.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: odd_history.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: odds.sql
package dbgen
@ -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,
@ -114,6 +143,34 @@ func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWith
return items, nil
}
const GetOddByID = `-- name: GetOddByID :one
SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event
WHERE id = $1
`
func (q *Queries) GetOddByID(ctx context.Context, id int64) (OddsMarketWithEvent, error) {
row := q.db.QueryRow(ctx, GetOddByID, id)
var i OddsMarketWithEvent
err := row.Scan(
&i.ID,
&i.EventID,
&i.MarketType,
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
&i.IsMonitored,
&i.IsLive,
&i.Status,
&i.Source,
)
return i, err
}
const GetOddsByEventID = `-- name: GetOddsByEventID :many
SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event
@ -219,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
`
@ -233,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,
@ -244,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,
@ -272,23 +358,50 @@ func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsW
return items, nil
}
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
WHERE market_id = $1
AND event_id = $2
AND company_id = $3
const GetOddsWithSettingsByID = `-- name: GetOddsWithSettingsByID :one
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 GetOddsWithSettingsByMarketIDParams struct {
MarketID string `json:"market_id"`
EventID string `json:"event_id"`
type GetOddsWithSettingsByIDParams struct {
ID int64 `json:"id"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (OddsMarketWithSetting, error) {
row := q.db.QueryRow(ctx, GetOddsWithSettingsByMarketID, arg.MarketID, arg.EventID, arg.CompanyID)
var i OddsMarketWithSetting
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 GetOddsWithSettingsByIDRow
err := row.Scan(
&i.ID,
&i.EventID,
@ -307,34 +420,68 @@ func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOdds
return i, err
}
const InsertOddSettings = `-- name: InsertOddSettings :exec
INSERT INTO company_odd_settings (
company_id,
odds_market_id,
is_active,
custom_raw_odds
)
VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
custom_raw_odds = EXCLUDED.custom_raw_odds
const GetOddsWithSettingsByMarketID = `-- name: GetOddsWithSettingsByMarketID :one
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
`
type InsertOddSettingsParams struct {
type GetOddsWithSettingsByMarketIDParams struct {
MarketID string `json:"market_id"`
EventID string `json:"event_id"`
CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
IsActive pgtype.Bool `json:"is_active"`
CustomRawOdds []byte `json:"custom_raw_odds"`
}
func (q *Queries) InsertOddSettings(ctx context.Context, arg InsertOddSettingsParams) error {
_, err := q.db.Exec(ctx, InsertOddSettings,
arg.CompanyID,
arg.OddsMarketID,
arg.IsActive,
arg.CustomRawOdds,
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 GetOddsWithSettingsByMarketIDRow
err := row.Scan(
&i.ID,
&i.EventID,
&i.MarketType,
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
&i.CompanyID,
&i.IsActive,
&i.RawOdds,
&i.UpdatedAt,
)
return err
return i, err
}
const InsertOddsMarket = `-- name: InsertOddsMarket :exec
@ -391,3 +538,33 @@ func (q *Queries) InsertOddsMarket(ctx context.Context, arg InsertOddsMarketPara
)
return err
}
const SaveOddSettings = `-- name: SaveOddSettings :exec
INSERT INTO company_odd_settings (
company_id,
odds_market_id,
is_active,
custom_raw_odds
)
VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
custom_raw_odds = EXCLUDED.custom_raw_odds
`
type SaveOddSettingsParams struct {
CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
IsActive pgtype.Bool `json:"is_active"`
CustomRawOdds []byte `json:"custom_raw_odds"`
}
func (q *Queries) SaveOddSettings(ctx context.Context, arg SaveOddSettingsParams) error {
_, err := q.db.Exec(ctx, SaveOddSettings,
arg.CompanyID,
arg.OddsMarketID,
arg.IsActive,
arg.CustomRawOdds,
)
return err
}

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: otp.sql
package dbgen

328
gen/db/raffle.sql.go Normal file
View File

@ -0,0 +1,328 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: raffle.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const AddSportRaffleFilter = `-- name: AddSportRaffleFilter :one
INSERT INTO raffle_sport_filters (raffle_id, sport_id, league_id)
VALUES ($1, $2, $3)
RETURNING id, raffle_id, sport_id, league_id
`
type AddSportRaffleFilterParams struct {
RaffleID int32 `json:"raffle_id"`
SportID int64 `json:"sport_id"`
LeagueID int64 `json:"league_id"`
}
func (q *Queries) AddSportRaffleFilter(ctx context.Context, arg AddSportRaffleFilterParams) (RaffleSportFilter, error) {
row := q.db.QueryRow(ctx, AddSportRaffleFilter, arg.RaffleID, arg.SportID, arg.LeagueID)
var i RaffleSportFilter
err := row.Scan(
&i.ID,
&i.RaffleID,
&i.SportID,
&i.LeagueID,
)
return i, err
}
const CheckValidSportRaffleFilter = `-- name: CheckValidSportRaffleFilter :one
SELECT COUNT(*) > 0 AS exists
FROM raffle_sport_filters
WHERE raffle_id = $1
AND sport_id = $2
AND league_id = $3
`
type CheckValidSportRaffleFilterParams struct {
RaffleID int32 `json:"raffle_id"`
SportID int64 `json:"sport_id"`
LeagueID int64 `json:"league_id"`
}
func (q *Queries) CheckValidSportRaffleFilter(ctx context.Context, arg CheckValidSportRaffleFilterParams) (bool, error) {
row := q.db.QueryRow(ctx, CheckValidSportRaffleFilter, arg.RaffleID, arg.SportID, arg.LeagueID)
var exists bool
err := row.Scan(&exists)
return exists, err
}
const CreateRaffle = `-- name: CreateRaffle :one
INSERT INTO raffles (company_id, name, expires_at, type)
VALUES ($1, $2, $3, $4)
RETURNING id, company_id, name, created_at, expires_at, type, status
`
type CreateRaffleParams struct {
CompanyID int32 `json:"company_id"`
Name string `json:"name"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
Type string `json:"type"`
}
func (q *Queries) CreateRaffle(ctx context.Context, arg CreateRaffleParams) (Raffle, error) {
row := q.db.QueryRow(ctx, CreateRaffle,
arg.CompanyID,
arg.Name,
arg.ExpiresAt,
arg.Type,
)
var i Raffle
err := row.Scan(
&i.ID,
&i.CompanyID,
&i.Name,
&i.CreatedAt,
&i.ExpiresAt,
&i.Type,
&i.Status,
)
return i, err
}
const CreateRaffleTicket = `-- name: CreateRaffleTicket :one
INSERT INTO raffle_tickets (raffle_id, user_id)
VALUES ($1, $2)
RETURNING id, raffle_id, user_id, is_active
`
type CreateRaffleTicketParams struct {
RaffleID int32 `json:"raffle_id"`
UserID int32 `json:"user_id"`
}
func (q *Queries) CreateRaffleTicket(ctx context.Context, arg CreateRaffleTicketParams) (RaffleTicket, error) {
row := q.db.QueryRow(ctx, CreateRaffleTicket, arg.RaffleID, arg.UserID)
var i RaffleTicket
err := row.Scan(
&i.ID,
&i.RaffleID,
&i.UserID,
&i.IsActive,
)
return i, err
}
const CreateRaffleWinner = `-- name: CreateRaffleWinner :one
INSERT INTO raffle_winners (raffle_id, user_id, rank)
VALUES ($1, $2, $3)
RETURNING id, raffle_id, user_id, rank, created_at
`
type CreateRaffleWinnerParams struct {
RaffleID int32 `json:"raffle_id"`
UserID int32 `json:"user_id"`
Rank int32 `json:"rank"`
}
func (q *Queries) CreateRaffleWinner(ctx context.Context, arg CreateRaffleWinnerParams) (RaffleWinner, error) {
row := q.db.QueryRow(ctx, CreateRaffleWinner, arg.RaffleID, arg.UserID, arg.Rank)
var i RaffleWinner
err := row.Scan(
&i.ID,
&i.RaffleID,
&i.UserID,
&i.Rank,
&i.CreatedAt,
)
return i, err
}
const DeleteRaffle = `-- name: DeleteRaffle :one
DELETE FROM raffles
WHERE id = $1
RETURNING id, company_id, name, created_at, expires_at, type, status
`
func (q *Queries) DeleteRaffle(ctx context.Context, id int32) (Raffle, error) {
row := q.db.QueryRow(ctx, DeleteRaffle, id)
var i Raffle
err := row.Scan(
&i.ID,
&i.CompanyID,
&i.Name,
&i.CreatedAt,
&i.ExpiresAt,
&i.Type,
&i.Status,
)
return i, err
}
const GetRaffleStanding = `-- name: GetRaffleStanding :many
SELECT
u.id AS user_id,
rt.raffle_id,
u.first_name,
u.last_name,
u.phone_number,
u.email,
COUNT(*) AS ticket_count
FROM raffle_tickets rt
JOIN users u ON rt.user_id = u.id
WHERE rt.is_active = true
AND rt.raffle_id = $1
GROUP BY u.id, rt.raffle_id, u.first_name, u.last_name, u.phone_number, u.email
ORDER BY ticket_count DESC
LIMIT $2
`
type GetRaffleStandingParams struct {
RaffleID int32 `json:"raffle_id"`
Limit int32 `json:"limit"`
}
type GetRaffleStandingRow struct {
UserID int64 `json:"user_id"`
RaffleID int32 `json:"raffle_id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
PhoneNumber pgtype.Text `json:"phone_number"`
Email pgtype.Text `json:"email"`
TicketCount int64 `json:"ticket_count"`
}
func (q *Queries) GetRaffleStanding(ctx context.Context, arg GetRaffleStandingParams) ([]GetRaffleStandingRow, error) {
rows, err := q.db.Query(ctx, GetRaffleStanding, arg.RaffleID, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetRaffleStandingRow
for rows.Next() {
var i GetRaffleStandingRow
if err := rows.Scan(
&i.UserID,
&i.RaffleID,
&i.FirstName,
&i.LastName,
&i.PhoneNumber,
&i.Email,
&i.TicketCount,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetRafflesOfCompany = `-- name: GetRafflesOfCompany :many
SELECT id, company_id, name, created_at, expires_at, type, status FROM raffles WHERE company_id = $1
`
func (q *Queries) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]Raffle, error) {
rows, err := q.db.Query(ctx, GetRafflesOfCompany, companyID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Raffle
for rows.Next() {
var i Raffle
if err := rows.Scan(
&i.ID,
&i.CompanyID,
&i.Name,
&i.CreatedAt,
&i.ExpiresAt,
&i.Type,
&i.Status,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetUserRaffleTickets = `-- name: GetUserRaffleTickets :many
SELECT
rt.id AS ticket_id,
rt.user_id,
r.name,
r.type,
r.expires_at,
r.status
FROM raffle_tickets rt
JOIN raffles r ON rt.raffle_id = r.id
WHERE rt.user_id = $1
`
type GetUserRaffleTicketsRow struct {
TicketID int32 `json:"ticket_id"`
UserID int32 `json:"user_id"`
Name string `json:"name"`
Type string `json:"type"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
Status string `json:"status"`
}
func (q *Queries) GetUserRaffleTickets(ctx context.Context, userID int32) ([]GetUserRaffleTicketsRow, error) {
rows, err := q.db.Query(ctx, GetUserRaffleTickets, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserRaffleTicketsRow
for rows.Next() {
var i GetUserRaffleTicketsRow
if err := rows.Scan(
&i.TicketID,
&i.UserID,
&i.Name,
&i.Type,
&i.ExpiresAt,
&i.Status,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const SetRaffleComplete = `-- name: SetRaffleComplete :exec
UPDATE raffles
SET status = 'completed'
WHERE id = $1
`
func (q *Queries) SetRaffleComplete(ctx context.Context, id int32) error {
_, err := q.db.Exec(ctx, SetRaffleComplete, id)
return err
}
const UpdateRaffleTicketStatus = `-- name: UpdateRaffleTicketStatus :exec
UPDATE raffle_tickets
SET is_active = $1
WHERE id = $2
`
type UpdateRaffleTicketStatusParams struct {
IsActive pgtype.Bool `json:"is_active"`
ID int32 `json:"id"`
}
func (q *Queries) UpdateRaffleTicketStatus(ctx context.Context, arg UpdateRaffleTicketStatusParams) error {
_, err := q.db.Exec(ctx, UpdateRaffleTicketStatus, arg.IsActive, arg.ID)
return err
}

View File

@ -1,335 +1,254 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: referal.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateReferral = `-- name: CreateReferral :one
INSERT INTO referrals (
const CreateReferralCode = `-- name: CreateReferralCode :one
INSERT INTO referral_codes (
referral_code,
referrer_id,
status,
reward_amount,
expires_at
) VALUES (
$1, $2, $3, $4, $5
) RETURNING id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at
company_id,
number_of_referrals,
reward_amount
)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at
`
type CreateReferralParams struct {
type CreateReferralCodeParams struct {
ReferralCode string `json:"referral_code"`
ReferrerID string `json:"referrer_id"`
Status Referralstatus `json:"status"`
RewardAmount pgtype.Numeric `json:"reward_amount"`
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
ReferrerID int64 `json:"referrer_id"`
CompanyID int64 `json:"company_id"`
NumberOfReferrals int64 `json:"number_of_referrals"`
RewardAmount int64 `json:"reward_amount"`
}
func (q *Queries) CreateReferral(ctx context.Context, arg CreateReferralParams) (Referral, error) {
row := q.db.QueryRow(ctx, CreateReferral,
func (q *Queries) CreateReferralCode(ctx context.Context, arg CreateReferralCodeParams) (ReferralCode, error) {
row := q.db.QueryRow(ctx, CreateReferralCode,
arg.ReferralCode,
arg.ReferrerID,
arg.Status,
arg.CompanyID,
arg.NumberOfReferrals,
arg.RewardAmount,
arg.ExpiresAt,
)
var i Referral
var i ReferralCode
err := row.Scan(
&i.ID,
&i.ReferralCode,
&i.ReferrerID,
&i.ReferredID,
&i.Status,
&i.CompanyID,
&i.IsActive,
&i.NumberOfReferrals,
&i.RewardAmount,
&i.CashbackAmount,
&i.CreatedAt,
&i.UpdatedAt,
&i.ExpiresAt,
)
return i, err
}
const CreateReferralSettings = `-- name: CreateReferralSettings :one
INSERT INTO referral_settings (
referral_reward_amount,
cashback_percentage,
max_referrals,
bet_referral_bonus_percentage,
expires_after_days,
updated_by
) VALUES (
$1, $2, $3, $4, $5, $6
) RETURNING id, referral_reward_amount, cashback_percentage, bet_referral_bonus_percentage, max_referrals, expires_after_days, updated_by, created_at, updated_at, version
const CreateUserReferral = `-- name: CreateUserReferral :one
INSERT INTO user_referrals (referred_id, referral_code_id)
VALUES ($1, $2)
RETURNING id, referred_id, referral_code_id, created_at
`
type CreateReferralSettingsParams struct {
ReferralRewardAmount pgtype.Numeric `json:"referral_reward_amount"`
CashbackPercentage pgtype.Numeric `json:"cashback_percentage"`
MaxReferrals int32 `json:"max_referrals"`
BetReferralBonusPercentage pgtype.Numeric `json:"bet_referral_bonus_percentage"`
ExpiresAfterDays int32 `json:"expires_after_days"`
UpdatedBy string `json:"updated_by"`
type CreateUserReferralParams struct {
ReferredID int64 `json:"referred_id"`
ReferralCodeID int64 `json:"referral_code_id"`
}
func (q *Queries) CreateReferralSettings(ctx context.Context, arg CreateReferralSettingsParams) (ReferralSetting, error) {
row := q.db.QueryRow(ctx, CreateReferralSettings,
arg.ReferralRewardAmount,
arg.CashbackPercentage,
arg.MaxReferrals,
arg.BetReferralBonusPercentage,
arg.ExpiresAfterDays,
arg.UpdatedBy,
)
var i ReferralSetting
func (q *Queries) CreateUserReferral(ctx context.Context, arg CreateUserReferralParams) (UserReferral, error) {
row := q.db.QueryRow(ctx, CreateUserReferral, arg.ReferredID, arg.ReferralCodeID)
var i UserReferral
err := row.Scan(
&i.ID,
&i.ReferralRewardAmount,
&i.CashbackPercentage,
&i.BetReferralBonusPercentage,
&i.MaxReferrals,
&i.ExpiresAfterDays,
&i.UpdatedBy,
&i.CreatedAt,
&i.UpdatedAt,
&i.Version,
)
return i, err
}
const GetActiveReferralByReferrerID = `-- name: GetActiveReferralByReferrerID :one
SELECT id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at FROM referrals WHERE referrer_id = $1 AND status = 'PENDING' LIMIT 1
`
func (q *Queries) GetActiveReferralByReferrerID(ctx context.Context, referrerID string) (Referral, error) {
row := q.db.QueryRow(ctx, GetActiveReferralByReferrerID, referrerID)
var i Referral
err := row.Scan(
&i.ID,
&i.ReferralCode,
&i.ReferrerID,
&i.ReferredID,
&i.Status,
&i.RewardAmount,
&i.CashbackAmount,
&i.ReferralCodeID,
&i.CreatedAt,
&i.UpdatedAt,
&i.ExpiresAt,
)
return i, err
}
const GetReferralByCode = `-- name: GetReferralByCode :one
SELECT id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at FROM referrals
const GetReferralCode = `-- name: GetReferralCode :one
SELECT id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at
FROM referral_codes
WHERE referral_code = $1
`
func (q *Queries) GetReferralByCode(ctx context.Context, referralCode string) (Referral, error) {
row := q.db.QueryRow(ctx, GetReferralByCode, referralCode)
var i Referral
func (q *Queries) GetReferralCode(ctx context.Context, referralCode string) (ReferralCode, error) {
row := q.db.QueryRow(ctx, GetReferralCode, referralCode)
var i ReferralCode
err := row.Scan(
&i.ID,
&i.ReferralCode,
&i.ReferrerID,
&i.ReferredID,
&i.Status,
&i.CompanyID,
&i.IsActive,
&i.NumberOfReferrals,
&i.RewardAmount,
&i.CashbackAmount,
&i.CreatedAt,
&i.UpdatedAt,
&i.ExpiresAt,
)
return i, err
}
const GetReferralByReferredID = `-- name: GetReferralByReferredID :one
SELECT id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at FROM referrals WHERE referred_id = $1 LIMIT 1
const GetReferralCodeByUser = `-- name: GetReferralCodeByUser :many
SELECt id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at
FROM referral_codes
WHERE referrer_id = $1
`
func (q *Queries) GetReferralByReferredID(ctx context.Context, referredID pgtype.Text) (Referral, error) {
row := q.db.QueryRow(ctx, GetReferralByReferredID, referredID)
var i Referral
err := row.Scan(
func (q *Queries) GetReferralCodeByUser(ctx context.Context, referrerID int64) ([]ReferralCode, error) {
rows, err := q.db.Query(ctx, GetReferralCodeByUser, referrerID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ReferralCode
for rows.Next() {
var i ReferralCode
if err := rows.Scan(
&i.ID,
&i.ReferralCode,
&i.ReferrerID,
&i.ReferredID,
&i.Status,
&i.CompanyID,
&i.IsActive,
&i.NumberOfReferrals,
&i.RewardAmount,
&i.CashbackAmount,
&i.CreatedAt,
&i.UpdatedAt,
&i.ExpiresAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetReferralStats = `-- name: GetReferralStats :one
SELECT COUNT(*) AS total_referrals,
COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned
FROM user_referrals
JOIN referral_codes ON referral_codes.id = referral_code_id
WHERE referrer_id = $1
AND company_id = $2
`
type GetReferralStatsParams struct {
ReferrerID int64 `json:"referrer_id"`
CompanyID int64 `json:"company_id"`
}
type GetReferralStatsRow struct {
TotalReferrals int64 `json:"total_referrals"`
TotalRewardEarned int64 `json:"total_reward_earned"`
}
func (q *Queries) GetReferralStats(ctx context.Context, arg GetReferralStatsParams) (GetReferralStatsRow, error) {
row := q.db.QueryRow(ctx, GetReferralStats, arg.ReferrerID, arg.CompanyID)
var i GetReferralStatsRow
err := row.Scan(&i.TotalReferrals, &i.TotalRewardEarned)
return i, err
}
const GetUserReferral = `-- name: GetUserReferral :one
SELECT id, referred_id, referral_code_id, created_at
FROM user_referrals
WHERE referred_id = $1
`
func (q *Queries) GetUserReferral(ctx context.Context, referredID int64) (UserReferral, error) {
row := q.db.QueryRow(ctx, GetUserReferral, referredID)
var i UserReferral
err := row.Scan(
&i.ID,
&i.ReferredID,
&i.ReferralCodeID,
&i.CreatedAt,
)
return i, err
}
const GetReferralCountByID = `-- name: GetReferralCountByID :one
SELECT count(*) FROM referrals WHERE referrer_id = $1
const GetUserReferralsByCode = `-- name: GetUserReferralsByCode :many
SELECT user_referrals.id, user_referrals.referred_id, user_referrals.referral_code_id, user_referrals.created_at
FROM user_referrals
JOIN referral_codes ON referral_codes.id = referral_code_id
WHERE referral_code = $1
`
func (q *Queries) GetReferralCountByID(ctx context.Context, referrerID string) (int64, error) {
row := q.db.QueryRow(ctx, GetReferralCountByID, referrerID)
func (q *Queries) GetUserReferralsByCode(ctx context.Context, referralCode string) ([]UserReferral, error) {
rows, err := q.db.Query(ctx, GetUserReferralsByCode, referralCode)
if err != nil {
return nil, err
}
defer rows.Close()
var items []UserReferral
for rows.Next() {
var i UserReferral
if err := rows.Scan(
&i.ID,
&i.ReferredID,
&i.ReferralCodeID,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetUserReferralsCount = `-- name: GetUserReferralsCount :one
SELECT COUNT(*)
FROM user_referrals
JOIN referral_codes ON referral_codes.id = referral_code_id
WHERE referrer_id = $1
`
func (q *Queries) GetUserReferralsCount(ctx context.Context, referrerID int64) (int64, error) {
row := q.db.QueryRow(ctx, GetUserReferralsCount, referrerID)
var count int64
err := row.Scan(&count)
return count, err
}
const GetReferralSettings = `-- name: GetReferralSettings :one
SELECT id, referral_reward_amount, cashback_percentage, bet_referral_bonus_percentage, max_referrals, expires_after_days, updated_by, created_at, updated_at, version FROM referral_settings
LIMIT 1
`
func (q *Queries) GetReferralSettings(ctx context.Context) (ReferralSetting, error) {
row := q.db.QueryRow(ctx, GetReferralSettings)
var i ReferralSetting
err := row.Scan(
&i.ID,
&i.ReferralRewardAmount,
&i.CashbackPercentage,
&i.BetReferralBonusPercentage,
&i.MaxReferrals,
&i.ExpiresAfterDays,
&i.UpdatedBy,
&i.CreatedAt,
&i.UpdatedAt,
&i.Version,
)
return i, err
}
const GetReferralStats = `-- name: GetReferralStats :one
SELECT
COUNT(*) as total_referrals,
COUNT(CASE WHEN status = 'COMPLETED' THEN 1 END) as completed_referrals,
COALESCE(SUM(reward_amount), 0) as total_reward_earned,
COALESCE(SUM(CASE WHEN status = 'PENDING' THEN reward_amount END), 0) as pending_rewards
FROM referrals
WHERE referrer_id = $1
`
type GetReferralStatsRow struct {
TotalReferrals int64 `json:"total_referrals"`
CompletedReferrals int64 `json:"completed_referrals"`
TotalRewardEarned interface{} `json:"total_reward_earned"`
PendingRewards interface{} `json:"pending_rewards"`
}
func (q *Queries) GetReferralStats(ctx context.Context, referrerID string) (GetReferralStatsRow, error) {
row := q.db.QueryRow(ctx, GetReferralStats, referrerID)
var i GetReferralStatsRow
err := row.Scan(
&i.TotalReferrals,
&i.CompletedReferrals,
&i.TotalRewardEarned,
&i.PendingRewards,
)
return i, err
}
const UpdateReferral = `-- name: UpdateReferral :one
UPDATE referrals
SET
referred_id = $2,
status = $3,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at
`
type UpdateReferralParams struct {
ID int64 `json:"id"`
ReferredID pgtype.Text `json:"referred_id"`
Status Referralstatus `json:"status"`
}
func (q *Queries) UpdateReferral(ctx context.Context, arg UpdateReferralParams) (Referral, error) {
row := q.db.QueryRow(ctx, UpdateReferral, arg.ID, arg.ReferredID, arg.Status)
var i Referral
err := row.Scan(
&i.ID,
&i.ReferralCode,
&i.ReferrerID,
&i.ReferredID,
&i.Status,
&i.RewardAmount,
&i.CashbackAmount,
&i.CreatedAt,
&i.UpdatedAt,
&i.ExpiresAt,
)
return i, err
}
const UpdateReferralCode = `-- name: UpdateReferralCode :exec
UPDATE users
SET
referral_code = $2,
UPDATE referral_codes
SET is_active = $2,
referral_code = $3,
number_of_referrals = $4,
reward_amount = $5,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`
type UpdateReferralCodeParams struct {
ID int64 `json:"id"`
ReferralCode pgtype.Text `json:"referral_code"`
IsActive bool `json:"is_active"`
ReferralCode string `json:"referral_code"`
NumberOfReferrals int64 `json:"number_of_referrals"`
RewardAmount int64 `json:"reward_amount"`
}
func (q *Queries) UpdateReferralCode(ctx context.Context, arg UpdateReferralCodeParams) error {
_, err := q.db.Exec(ctx, UpdateReferralCode, arg.ID, arg.ReferralCode)
_, err := q.db.Exec(ctx, UpdateReferralCode,
arg.ID,
arg.IsActive,
arg.ReferralCode,
arg.NumberOfReferrals,
arg.RewardAmount,
)
return err
}
const UpdateReferralSettings = `-- name: UpdateReferralSettings :one
UPDATE referral_settings
SET
referral_reward_amount = $2,
cashback_percentage = $3,
bet_referral_bonus_percentage= $4,
max_referrals = $5,
expires_after_days = $6,
updated_by = $7,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING id, referral_reward_amount, cashback_percentage, bet_referral_bonus_percentage, max_referrals, expires_after_days, updated_by, created_at, updated_at, version
`
type UpdateReferralSettingsParams struct {
ID int64 `json:"id"`
ReferralRewardAmount pgtype.Numeric `json:"referral_reward_amount"`
CashbackPercentage pgtype.Numeric `json:"cashback_percentage"`
BetReferralBonusPercentage pgtype.Numeric `json:"bet_referral_bonus_percentage"`
MaxReferrals int32 `json:"max_referrals"`
ExpiresAfterDays int32 `json:"expires_after_days"`
UpdatedBy string `json:"updated_by"`
}
func (q *Queries) UpdateReferralSettings(ctx context.Context, arg UpdateReferralSettingsParams) (ReferralSetting, error) {
row := q.db.QueryRow(ctx, UpdateReferralSettings,
arg.ID,
arg.ReferralRewardAmount,
arg.CashbackPercentage,
arg.BetReferralBonusPercentage,
arg.MaxReferrals,
arg.ExpiresAfterDays,
arg.UpdatedBy,
)
var i ReferralSetting
err := row.Scan(
&i.ID,
&i.ReferralRewardAmount,
&i.CashbackPercentage,
&i.BetReferralBonusPercentage,
&i.MaxReferrals,
&i.ExpiresAfterDays,
&i.UpdatedBy,
&i.CreatedAt,
&i.UpdatedAt,
&i.Version,
)
return i, err
}

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: report.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: result.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: result_log.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: settings.sql
package dbgen
@ -181,7 +181,9 @@ func (q *Queries) GetGlobalSettings(ctx context.Context) ([]GlobalSetting, error
}
const GetOverrideSettings = `-- name: GetOverrideSettings :many
SELECT gs.key, gs.value, gs.created_at, gs.updated_at,
SELECT gs.key,
gs.created_at,
gs.updated_at,
COALESCE(cs.value, gs.value) AS value
FROM global_settings gs
LEFT JOIN company_settings cs ON cs.key = gs.key
@ -190,10 +192,9 @@ FROM global_settings gs
type GetOverrideSettingsRow struct {
Key string `json:"key"`
Value string `json:"value"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Value_2 string `json:"value_2"`
Value string `json:"value"`
}
func (q *Queries) GetOverrideSettings(ctx context.Context, companyID int64) ([]GetOverrideSettingsRow, error) {
@ -207,10 +208,9 @@ func (q *Queries) GetOverrideSettings(ctx context.Context, companyID int64) ([]G
var i GetOverrideSettingsRow
if err := rows.Scan(
&i.Key,
&i.Value,
&i.CreatedAt,
&i.UpdatedAt,
&i.Value_2,
&i.Value,
); err != nil {
return nil, err
}

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: shop_transactions.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: ticket.sql
package dbgen

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: transfer.sql
package dbgen
@ -182,6 +182,40 @@ func (q *Queries) GetTransferByReference(ctx context.Context, referenceNumber st
return i, err
}
const GetTransferStats = `-- name: GetTransferStats :one
SELECT COUNT(*) AS total_transfers, COUNT(*) FILTER (
WHERE type = 'deposit'
) AS total_deposits,
COUNT(*) FILTER (
WHERE type = 'withdraw'
) AS total_withdraw,
COUNT(*) FILTER (
WHERE type = 'wallet'
) AS total_wallet_to_wallet
FROM wallet_transfer
WHERE sender_wallet_id = $1
OR receiver_wallet_id = $1
`
type GetTransferStatsRow struct {
TotalTransfers int64 `json:"total_transfers"`
TotalDeposits int64 `json:"total_deposits"`
TotalWithdraw int64 `json:"total_withdraw"`
TotalWalletToWallet int64 `json:"total_wallet_to_wallet"`
}
func (q *Queries) GetTransferStats(ctx context.Context, senderWalletID pgtype.Int8) (GetTransferStatsRow, error) {
row := q.db.QueryRow(ctx, GetTransferStats, senderWalletID)
var i GetTransferStatsRow
err := row.Scan(
&i.TotalTransfers,
&i.TotalDeposits,
&i.TotalWithdraw,
&i.TotalWalletToWallet,
)
return i, err
}
const GetTransfersByWallet = `-- name: GetTransfersByWallet :many
SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, session_id, status, payment_method, created_at, updated_at, first_name, last_name, phone_number
FROM wallet_transfer_details

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: user.sql
package dbgen
@ -163,7 +163,7 @@ func (q *Queries) DeleteUser(ctx context.Context, id int64) error {
}
const GetAdminByCompanyID = `-- name: GetAdminByCompanyID :one
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended
FROM companies
JOIN users ON companies.admin_id = users.id
where companies.id = $1
@ -187,8 +187,6 @@ func (q *Queries) GetAdminByCompanyID(ctx context.Context, id int64) (User, erro
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,
&i.ReferredBy,
)
return i, err
}
@ -388,7 +386,7 @@ func (q *Queries) GetUserByEmail(ctx context.Context, arg GetUserByEmailParams)
}
const GetUserByID = `-- name: GetUserByID :one
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended
FROM users
WHERE id = $1
`
@ -411,8 +409,6 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,
&i.ReferredBy,
)
return i, err
}
@ -587,7 +583,7 @@ SET password = $1,
WHERE (
email = $2
OR phone_number = $3
AND company_id = $4
AND company_id = $5
)
`
@ -596,6 +592,7 @@ type UpdatePasswordParams struct {
Email pgtype.Text `json:"email"`
PhoneNumber pgtype.Text `json:"phone_number"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CompanyID pgtype.Int8 `json:"company_id"`
}
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error {
@ -604,6 +601,7 @@ func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams)
arg.Email,
arg.PhoneNumber,
arg.UpdatedAt,
arg.CompanyID,
)
return err
}

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: virtual_games.sql
package dbgen
@ -12,12 +12,8 @@ import (
)
const AddFavoriteGame = `-- name: AddFavoriteGame :exec
INSERT INTO favorite_games (
user_id,
game_id,
created_at
) VALUES ($1, $2, NOW())
ON CONFLICT (user_id, game_id) DO NOTHING
INSERT INTO favorite_games (user_id, game_id, created_at)
VALUES ($1, $2, NOW()) ON CONFLICT (user_id, game_id) DO NOTHING
`
type AddFavoriteGameParams struct {
@ -56,11 +52,22 @@ INSERT INTO virtual_games (
bets,
thumbnail,
status
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
)
RETURNING
id,
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12
)
RETURNING id,
game_id,
provider_id,
name,
@ -142,10 +149,22 @@ INSERT INTO virtual_game_histories (
external_transaction_id,
reference_transaction_id,
status
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
) RETURNING
id,
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12
)
RETURNING id,
session_id,
user_id,
company_id,
@ -215,10 +234,21 @@ func (q *Queries) CreateVirtualGameHistory(ctx context.Context, arg CreateVirtua
const CreateVirtualGameProvider = `-- name: CreateVirtualGameProvider :one
INSERT INTO virtual_game_providers (
provider_id, provider_name, logo_dark, logo_light, enabled
) VALUES (
$1, $2, $3, $4, $5
) RETURNING id, provider_id, provider_name, logo_dark, logo_light, enabled, created_at, updated_at
provider_id,
provider_name,
logo_dark,
logo_light,
enabled
)
VALUES ($1, $2, $3, $4, $5)
RETURNING id,
provider_id,
provider_name,
logo_dark,
logo_light,
enabled,
created_at,
updated_at
`
type CreateVirtualGameProviderParams struct {
@ -253,10 +283,23 @@ func (q *Queries) CreateVirtualGameProvider(ctx context.Context, arg CreateVirtu
const CreateVirtualGameSession = `-- name: CreateVirtualGameSession :one
INSERT INTO virtual_game_sessions (
user_id, game_id, session_token, currency, status, expires_at
) VALUES (
$1, $2, $3, $4, $5, $6
) RETURNING id, user_id, game_id, session_token, currency, status, created_at, updated_at, expires_at
user_id,
game_id,
session_token,
currency,
status,
expires_at
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id,
user_id,
game_id,
session_token,
currency,
status,
created_at,
updated_at,
expires_at
`
type CreateVirtualGameSessionParams struct {
@ -294,10 +337,31 @@ func (q *Queries) CreateVirtualGameSession(ctx context.Context, arg CreateVirtua
const CreateVirtualGameTransaction = `-- name: CreateVirtualGameTransaction :one
INSERT INTO virtual_game_transactions (
session_id, user_id, company_id, provider, wallet_id, transaction_type, amount, currency, external_transaction_id, status
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
) RETURNING id, session_id, user_id, company_id, provider, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at
session_id,
user_id,
company_id,
provider,
wallet_id,
transaction_type,
amount,
currency,
external_transaction_id,
status
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id,
session_id,
user_id,
company_id,
provider,
wallet_id,
transaction_type,
amount,
currency,
external_transaction_id,
status,
created_at,
updated_at
`
type CreateVirtualGameTransactionParams struct {
@ -390,8 +454,7 @@ func (q *Queries) DeleteVirtualGameProvider(ctx context.Context, providerID stri
}
const GetAllVirtualGames = `-- name: GetAllVirtualGames :many
SELECT
vg.id,
SELECT vg.id,
vg.game_id,
vg.provider_id,
vp.provider_name,
@ -408,19 +471,29 @@ SELECT
vg.created_at,
vg.updated_at
FROM virtual_games vg
JOIN virtual_game_providers vp ON vg.provider_id = vp.provider_id
WHERE
($1::text IS NULL OR vg.category = $1) -- category filter (optional)
AND ($2::text IS NULL OR vg.name ILIKE '%' || $2 || '%') -- search by name (optional)
JOIN virtual_game_providers vp ON vg.provider_id = vp.provider_id
WHERE (
vg.category = $1
OR $1 IS NULL
)
AND (
name ILIKE '%' || $2 || '%'
OR $2 IS NULL
)
AND (
vg.provider_id = $3
OR $3 IS NULL
)
ORDER BY vg.created_at DESC
LIMIT $3 OFFSET $4
LIMIT $5 OFFSET $4
`
type GetAllVirtualGamesParams struct {
Column1 string `json:"column_1"`
Column2 string `json:"column_2"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
Category pgtype.Text `json:"category"`
Name pgtype.Text `json:"name"`
ProviderID pgtype.Text `json:"provider_id"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
type GetAllVirtualGamesRow struct {
@ -444,10 +517,11 @@ type GetAllVirtualGamesRow struct {
func (q *Queries) GetAllVirtualGames(ctx context.Context, arg GetAllVirtualGamesParams) ([]GetAllVirtualGamesRow, error) {
rows, err := q.db.Query(ctx, GetAllVirtualGames,
arg.Column1,
arg.Column2,
arg.Limit,
arg.Category,
arg.Name,
arg.ProviderID,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, err
@ -485,7 +559,14 @@ func (q *Queries) GetAllVirtualGames(ctx context.Context, arg GetAllVirtualGames
}
const GetVirtualGameProviderByID = `-- name: GetVirtualGameProviderByID :one
SELECT id, provider_id, provider_name, logo_dark, logo_light, enabled, created_at, updated_at
SELECT id,
provider_id,
provider_name,
logo_dark,
logo_light,
enabled,
created_at,
updated_at
FROM virtual_game_providers
WHERE provider_id = $1
`
@ -507,7 +588,15 @@ func (q *Queries) GetVirtualGameProviderByID(ctx context.Context, providerID str
}
const GetVirtualGameSessionByToken = `-- name: GetVirtualGameSessionByToken :one
SELECT id, user_id, game_id, session_token, currency, status, created_at, updated_at, expires_at
SELECT id,
user_id,
game_id,
session_token,
currency,
status,
created_at,
updated_at,
expires_at
FROM virtual_game_sessions
WHERE session_token = $1
`
@ -530,18 +619,18 @@ func (q *Queries) GetVirtualGameSessionByToken(ctx context.Context, sessionToken
}
const GetVirtualGameSummaryInRange = `-- name: GetVirtualGameSummaryInRange :many
SELECT
c.name AS company_name,
SELECT c.name AS company_name,
vg.name AS game_name,
COUNT(vgt.id) AS number_of_bets,
COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum
FROM virtual_game_transactions vgt
JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
JOIN virtual_games vg ON vgs.game_id = vg.id
JOIN companies c ON vgt.company_id = c.id
JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id
JOIN virtual_games vg ON vgs.game_id = vg.id
JOIN companies c ON vgt.company_id = c.id
WHERE vgt.transaction_type = 'BET'
AND vgt.created_at BETWEEN $1 AND $2
GROUP BY c.name, vg.name
GROUP BY c.name,
vg.name
`
type GetVirtualGameSummaryInRangeParams struct {
@ -582,7 +671,17 @@ func (q *Queries) GetVirtualGameSummaryInRange(ctx context.Context, arg GetVirtu
}
const GetVirtualGameTransactionByExternalID = `-- name: GetVirtualGameTransactionByExternalID :one
SELECT id, session_id, user_id, wallet_id, transaction_type, amount, currency, external_transaction_id, status, created_at, updated_at
SELECT id,
session_id,
user_id,
wallet_id,
transaction_type,
amount,
currency,
external_transaction_id,
status,
created_at,
updated_at
FROM virtual_game_transactions
WHERE external_transaction_id = $1
`
@ -647,7 +746,14 @@ func (q *Queries) ListFavoriteGames(ctx context.Context, userID int64) ([]int64,
}
const ListVirtualGameProviders = `-- name: ListVirtualGameProviders :many
SELECT id, provider_id, provider_name, logo_dark, logo_light, enabled, created_at, updated_at
SELECT id,
provider_id,
provider_name,
logo_dark,
logo_light,
enabled,
created_at,
updated_at
FROM virtual_game_providers
ORDER BY created_at DESC
LIMIT $1 OFFSET $2
@ -689,7 +795,8 @@ func (q *Queries) ListVirtualGameProviders(ctx context.Context, arg ListVirtualG
const RemoveFavoriteGame = `-- name: RemoveFavoriteGame :exec
DELETE FROM favorite_games
WHERE user_id = $1 AND game_id = $2
WHERE user_id = $1
AND game_id = $2
`
type RemoveFavoriteGameParams struct {
@ -707,7 +814,14 @@ UPDATE virtual_game_providers
SET enabled = $2,
updated_at = CURRENT_TIMESTAMP
WHERE provider_id = $1
RETURNING id, provider_id, provider_name, logo_dark, logo_light, enabled, created_at, updated_at
RETURNING id,
provider_id,
provider_name,
logo_dark,
logo_light,
enabled,
created_at,
updated_at
`
type UpdateVirtualGameProviderEnabledParams struct {
@ -733,7 +847,8 @@ func (q *Queries) UpdateVirtualGameProviderEnabled(ctx context.Context, arg Upda
const UpdateVirtualGameSessionStatus = `-- name: UpdateVirtualGameSessionStatus :exec
UPDATE virtual_game_sessions
SET status = $2, updated_at = CURRENT_TIMESTAMP
SET status = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`
@ -749,7 +864,8 @@ func (q *Queries) UpdateVirtualGameSessionStatus(ctx context.Context, arg Update
const UpdateVirtualGameTransactionStatus = `-- name: UpdateVirtualGameTransactionStatus :exec
UPDATE virtual_game_transactions
SET status = $2, updated_at = CURRENT_TIMESTAMP
SET status = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// sqlc v1.30.0
// source: wallet.sql
package dbgen
@ -50,7 +50,7 @@ INSERT INTO wallets (
type
)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance
RETURNING id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at
`
type CreateWalletParams struct {
@ -82,8 +82,6 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal
&i.IsActive,
&i.CreatedAt,
&i.UpdatedAt,
&i.BonusBalance,
&i.CashBalance,
)
return i, err
}
@ -188,7 +186,7 @@ func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDet
}
const GetAllWallets = `-- name: GetAllWallets :many
SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance
SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at
FROM wallets
`
@ -213,8 +211,6 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
&i.IsActive,
&i.CreatedAt,
&i.UpdatedAt,
&i.BonusBalance,
&i.CashBalance,
); err != nil {
return nil, err
}
@ -319,7 +315,7 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (Cust
}
const GetWalletByID = `-- name: GetWalletByID :one
SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance
SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at
FROM wallets
WHERE id = $1
`
@ -339,14 +335,12 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
&i.IsActive,
&i.CreatedAt,
&i.UpdatedAt,
&i.BonusBalance,
&i.CashBalance,
)
return i, err
}
const GetWalletByUserID = `-- name: GetWalletByUserID :many
SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance
SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at
FROM wallets
WHERE user_id = $1
`
@ -372,8 +366,6 @@ func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet
&i.IsActive,
&i.CreatedAt,
&i.UpdatedAt,
&i.BonusBalance,
&i.CashBalance,
); err != nil {
return nil, err
}

View File

@ -10,3 +10,10 @@ type RefreshToken struct {
CreatedAt time.Time
Revoked bool
}
// I used this because i was getting an error with the ValidInt64
// when it was being unmarshaled by the jwt
type NullJwtInt64 struct {
Value int64
Valid bool
}

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

172
internal/domain/bonus.go Normal file
View File

@ -0,0 +1,172 @@
package domain
import (
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/jackc/pgx/v5/pgtype"
)
type BonusType string
var (
WelcomeBonus BonusType = "welcome_bonus"
DepositBonus BonusType = "deposit_bonus"
)
type UserBonus struct {
ID int64
Name string
Description string
UserID int64
Type BonusType
RewardAmount Currency
IsClaimed bool
ExpiresAt time.Time
CreatedAt time.Time
UpdatedAt time.Time
}
type UserBonusRes struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
UserID int64 `json:"user_id"`
Type BonusType `json:"type"`
RewardAmount float32 `json:"reward_amount"`
IsClaimed bool `json:"is_claimed"`
ExpiresAt time.Time `json:"expires_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func ConvertToBonusRes(bonus UserBonus) UserBonusRes {
return UserBonusRes{
ID: bonus.ID,
Name: bonus.Name,
Description: bonus.Description,
Type: bonus.Type,
UserID: bonus.UserID,
RewardAmount: bonus.RewardAmount.Float32(),
IsClaimed: bonus.IsClaimed,
ExpiresAt: bonus.ExpiresAt,
CreatedAt: bonus.CreatedAt,
UpdatedAt: bonus.UpdatedAt,
}
}
func ConvertToBonusResList(bonuses []UserBonus) []UserBonusRes {
result := make([]UserBonusRes, len(bonuses))
for i, bonus := range bonuses {
result[i] = ConvertToBonusRes(bonus)
}
return result
}
type CreateBonus struct {
Name string
Description string
Type BonusType
UserID int64
RewardAmount Currency
ExpiresAt time.Time
}
// type CreateBonusReq struct {
// Name string `json:"name"`
// Description string `json:"description"`
// Type BonusType `json:"type"`
// UserID int64 `json:"user_id"`
// RewardAmount float32 `json:"reward_amount"`
// ExpiresAt time.Time `json:"expires_at"`
// }
// func ConvertCreateBonusReq(bonus CreateBonusReq, companyID int64) CreateBonus {
// return CreateBonus{
// Name: bonus.Name,
// Description: bonus.Description,
// Type: bonus.Type,
// UserID: bonus.UserID,
// RewardAmount: ToCurrency(bonus.RewardAmount),
// ExpiresAt: bonus.ExpiresAt,
// }
// }
func ConvertCreateBonus(bonus CreateBonus) dbgen.CreateUserBonusParams {
return dbgen.CreateUserBonusParams{
Name: bonus.Name,
Description: bonus.Description,
Type: string(bonus.Type),
UserID: bonus.UserID,
RewardAmount: int64(bonus.RewardAmount),
ExpiresAt: pgtype.Timestamp{
Time: bonus.ExpiresAt,
Valid: true,
},
}
}
func ConvertDBBonus(bonus dbgen.UserBonuse) UserBonus {
return UserBonus{
ID: bonus.ID,
Name: bonus.Name,
Description: bonus.Description,
Type: BonusType(bonus.Type),
UserID: bonus.UserID,
RewardAmount: Currency(bonus.RewardAmount),
IsClaimed: bonus.IsClaimed,
ExpiresAt: bonus.ExpiresAt.Time,
CreatedAt: bonus.CreatedAt.Time,
UpdatedAt: bonus.UpdatedAt.Time,
}
}
func ConvertDBBonuses(bonuses []dbgen.UserBonuse) []UserBonus {
result := make([]UserBonus, len(bonuses))
for i, bonus := range bonuses {
result[i] = ConvertDBBonus(bonus)
}
return result
}
type BonusFilter struct {
UserID ValidInt64
CompanyID ValidInt64
Limit ValidInt
Offset ValidInt
}
type BonusStats struct {
TotalBonus int64
TotalRewardAmount Currency
ClaimedBonuses int64
ExpiredBonuses int64
}
type BonusStatsRes struct {
TotalBonus int64 `json:"total_bonus"`
TotalRewardAmount float32 `json:"total_reward_amount"`
ClaimedBonuses int64 `json:"claimed_bonuses"`
ExpiredBonuses int64 `json:"expired_bonuses"`
}
func ConvertToBonusStatsRes(bonus BonusStats) BonusStatsRes {
return BonusStatsRes{
TotalBonus: bonus.TotalBonus,
TotalRewardAmount: bonus.TotalRewardAmount.Float32(),
ClaimedBonuses: bonus.ClaimedBonuses,
ExpiredBonuses: bonus.ExpiredBonuses,
}
}
func ConvertDBBonusStats(stats dbgen.GetBonusStatsRow) BonusStats {
return BonusStats{
TotalBonus: stats.TotalBonuses,
TotalRewardAmount: Currency(stats.TotalRewardEarned),
ClaimedBonuses: stats.ClaimedBonuses,
ExpiredBonuses: stats.ExpiredBonuses,
}
}

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,12 +97,12 @@ 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"`
Score string `json:"score,omitempty"`
MatchMinute int `json:"match_minute,omitempty"`
TimerStatus string `json:"timer_status,omitempty"`
AddedTime int `json:"added_time,omitempty"`
MatchPeriod int `json:"match_period,omitempty"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
Score string `json:"score"`
MatchMinute int `json:"match_minute"`
TimerStatus string `json:"timer_status"`
AddedTime int `json:"added_time"`
MatchPeriod int `json:"match_period"`
IsLive bool `json:"is_live"`
FetchedAt time.Time `json:"fetched_at"`
}
@ -122,10 +122,13 @@ type EventWithSettings struct {
StartTime time.Time
Source EventSource
Status EventStatus
IsFeatured bool
IsMonitored bool
IsFeatured bool
IsActive bool
WinningUpperLimit int32
DefaultIsFeatured bool
DefaultIsActive bool
DefaultWinningUpperLimit int64
Score ValidString
MatchMinute ValidInt
TimerStatus ValidString
@ -152,6 +155,7 @@ type CreateEvent struct {
IsLive bool
Status EventStatus
Source EventSource
DefaultWinningUpperLimit int64
}
type EventWithSettingsRes struct {
@ -170,10 +174,13 @@ type EventWithSettingsRes struct {
StartTime time.Time `json:"start_time"`
Source EventSource `json:"source"`
Status EventStatus `json:"status"`
IsFeatured bool `json:"is_featured"`
IsMonitored bool `json:"is_monitored"`
IsFeatured bool `json:"is_featured"`
IsActive bool `json:"is_active"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
Score string `json:"score,omitempty"`
MatchMinute int `json:"match_minute,omitempty"`
TimerStatus string `json:"timer_status,omitempty"`
@ -212,6 +219,7 @@ type EventFilter struct {
Offset ValidInt32
MatchStatus ValidString // e.g., "upcoming", "in_play", "ended"
Featured ValidBool
Active ValidBool
}
func ConvertDBEvent(event dbgen.EventWithCountry) BaseEvent {
@ -288,11 +296,12 @@ func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
IsLive: e.IsLive,
Status: string(e.Status),
Source: string(e.Source),
DefaultWinningUpperLimit: e.DefaultWinningUpperLimit,
}
}
func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.InsertEventSettingsParams {
return dbgen.InsertEventSettingsParams{
func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.SaveEventSettingsParams {
return dbgen.SaveEventSettingsParams{
CompanyID: eventSettings.CompanyID,
EventID: eventSettings.EventID,
IsActive: eventSettings.IsActive.ToPG(),
@ -324,7 +333,9 @@ func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings {
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
WinningUpperLimit: event.WinningUpperLimit,
DefaultIsFeatured: event.DefaultIsFeatured,
DefaultIsActive: event.DefaultIsActive,
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
Score: ValidString{
Value: event.Score.String,
Valid: event.Score.Valid,
@ -359,8 +370,8 @@ func ConvertDBEventWithSettings(events []dbgen.EventWithSetting) []EventWithSett
return result
}
func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.UpdateEventSettingsParams {
return dbgen.UpdateEventSettingsParams{
func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.SaveEventSettingsParams {
return dbgen.SaveEventSettingsParams{
EventID: event.EventID,
CompanyID: event.CompanyID,
IsActive: event.IsActive.ToPG(),
@ -427,6 +438,9 @@ func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
DefaultIsFeatured: event.DefaultIsFeatured,
DefaultIsActive: event.DefaultIsActive,
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
WinningUpperLimit: event.WinningUpperLimit,
Score: event.Score.Value,
MatchMinute: event.MatchMinute.Value,

View File

@ -0,0 +1,27 @@
package domain
import (
"encoding/json"
"fmt"
)
// Custom type for fields that can be string or int
type StringOrNumber string
func (s *StringOrNumber) UnmarshalJSON(data []byte) error {
// Try as string
var str string
if err := json.Unmarshal(data, &str); err == nil {
*s = StringOrNumber(str)
return nil
}
// Try as number
var num json.Number
if err := json.Unmarshal(data, &num); err == nil {
*s = StringOrNumber(num.String())
return nil
}
return fmt.Errorf("StringOrNumber: cannot unmarshal %s", string(data))
}

View File

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

View File

@ -32,6 +32,7 @@ const (
NOTIFICATION_TYPE_BET_RESULT NotificationType = "bet_result"
NOTIFICATION_TYPE_TRANSFER_REJECTED NotificationType = "transfer_rejected"
NOTIFICATION_TYPE_APPROVAL_REQUIRED NotificationType = "approval_required"
NOTIFICATION_TYPE_BONUS_AWARDED NotificationType = "bonus_awarded"
NotificationRecieverSideAdmin NotificationRecieverSide = "admin"
NotificationRecieverSideCustomer NotificationRecieverSide = "customer"
@ -73,7 +74,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 StringOrNumber `json:"id"`
Name string `json:"name"`
Odds []json.RawMessage `json:"odds"`
Header string `json:"header,omitempty"`

View File

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

83
internal/domain/raffle.go Normal file
View File

@ -0,0 +1,83 @@
package domain
import "time"
type Raffle struct {
ID int32
CompanyID int32
Name string
CreatedAt time.Time
ExpiresAt time.Time
Type string
Status string
}
type RaffleFilter struct {
// requireds will depend on type of raffle (sport or game)
Type string `json:"type" validate:"required,oneof=sport game"`
RaffleID int32 `json:"raffle_id" validate:"required"`
SportID int32 `json:"sport_id" validate:"required_if=Type sport"`
LeagueID int32 `json:"league_id" validate:"required_if=Type sport"`
GameID string `json:"game_id" validate:"required_if=Type game"`
}
type RaffleStanding struct {
UserID int64
RaffleID int32
FirstName string
LastName string
PhoneNumber string
Email string
TicketCount int64
}
type RaffleStandingRes struct {
UserID int64 `json:"user_id"`
RaffleID int32 `json:"raffle_id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
PhoneNumber string `json:"phone_number"`
Email string `json:"email"`
TicketCount int64 `json:"ticket_count"`
}
type RaffleWinnerParams struct {
RaffleID int32
UserID int32
Rank int32
}
type RaffleTicket struct {
ID int32
RaffleID int32
UserID int32
IsActive bool
}
type RaffleTicketRes struct {
TicketID int32
UserID int32
Name string
Type string
ExpiresAt time.Time
Status string
}
type CreateRaffle struct {
CompanyID int32 `json:"company_id" validate:"required"`
Name string `json:"name" validate:"required"`
ExpiresAt *time.Time `json:"expires_at" validate:"required"`
Type string `json:"type" validate:"required"`
}
type CreateRaffleTicket struct {
RaffleID int32 `json:"raffle_id" validate:"required"`
UserID int32 `json:"user_id" validate:"required"`
}
// aside from ID, atleast one of the fields should be required
type UpdateRaffleParams struct {
ID int32 `json:"id" validate:"required"`
Name string `json:"name" validate:"required_without_all=ExpiresAt"`
ExpiresAt *time.Time `json:"expires_at" validate:"required_without_all=Name"`
}

View File

@ -1,73 +1,186 @@
package domain
import (
"database/sql/driver"
"fmt"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
)
type ReferralStatus string
const (
ReferralPending ReferralStatus = "PENDING"
ReferralCompleted ReferralStatus = "COMPLETED"
ReferralExpired ReferralStatus = "EXPIRED"
ReferralCancelled ReferralStatus = "CANCELLED"
)
func (rs *ReferralStatus) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*rs = ReferralStatus(s)
case string:
*rs = ReferralStatus(s)
default:
return fmt.Errorf("unsupported scan type for ReferralStatus: %T", src)
}
return nil
type ReferralCode struct {
ID int64
ReferrerID int64
ReferralCode string
CompanyID int64
NumberOfReferrals int64
RewardAmount Currency
CreatedAt time.Time
UpdatedAt time.Time
}
func (rs ReferralStatus) Value() (driver.Value, error) {
return string(rs), nil
type ReferralCodeRes struct {
ID int64 `json:"id"`
ReferrerID int64 `json:"referrer_id"`
ReferralCode string `json:"referral_code"`
CompanyID int64 `json:"company_id"`
NumberOfReferrals int64 `json:"number_of_referrals"`
RewardAmount float32 `json:"reward_amount"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type CreateReferralCode struct {
ReferrerID int64
ReferralCode string
CompanyID int64
NumberOfReferrals int64
RewardAmount Currency
}
type UserReferral struct {
ReferredID int64
ReferralCodeID int64
}
type CreateUserReferrals struct {
ReferredID int64
ReferralCodeID int64
}
type UpdateReferralCode struct {
ID int64
IsActive bool
ReferralCode string
RewardAmount Currency
NumberOfReferrals int64
}
type ReferralStats struct {
TotalReferrals int
CompletedReferrals int
TotalRewardEarned float64
PendingRewards float64
TotalReferrals int64
TotalRewardEarned Currency
}
type ReferralSettings struct {
ID int64
ReferralRewardAmount float64
CashbackPercentage float64
BetReferralBonusPercentage float64
MaxReferrals int32
ExpiresAfterDays int32
UpdatedBy string
CreatedAt time.Time
UpdatedAt time.Time
Version int32
type ReferralStatsRes struct {
TotalReferrals int64 `json:"total_referrals"`
TotalRewardEarned float32 `json:"total_reward_earned"`
}
type ReferralSettingsReq struct {
ReferralRewardAmount float64 `json:"referral_reward_amount" validate:"required"`
CashbackPercentage float64 `json:"cashback_percentage" validate:"required"`
MaxReferrals int32 `json:"max_referrals" validate:"required"`
ExpiresAfterDays int32 `json:"expires_afterdays" validate:"required"`
UpdatedBy string `json:"updated_by" validate:"required"`
// type ReferralSettings struct {
// ID int64
// ReferralRewardAmount float64
// CashbackPercentage float64
// BetReferralBonusPercentage float64
// MaxReferrals int32
// ExpiresAfterDays int32
// UpdatedBy string
// CreatedAt time.Time
// UpdatedAt time.Time
// Version int32
// }
// type ReferralSettingsReq struct {
// ReferralRewardAmount float64 `json:"referral_reward_amount" validate:"required"`
// CashbackPercentage float64 `json:"cashback_percentage" validate:"required"`
// MaxReferrals int32 `json:"max_referrals" validate:"required"`
// UpdatedBy string `json:"updated_by" validate:"required"`
// }
func ConvertCreateReferralCode(code CreateReferralCode) dbgen.CreateReferralCodeParams {
return dbgen.CreateReferralCodeParams{
ReferralCode: code.ReferralCode,
ReferrerID: code.ReferrerID,
CompanyID: code.CompanyID,
NumberOfReferrals: code.NumberOfReferrals,
RewardAmount: int64(code.RewardAmount),
}
}
type Referral struct {
ID int64
ReferralCode string
ReferrerID string
ReferredID *string
Status ReferralStatus
RewardAmount float64
CashbackAmount float64
CreatedAt time.Time
UpdatedAt time.Time
ExpiresAt time.Time
func ConvertDBReferralCode(code dbgen.ReferralCode) ReferralCode {
return ReferralCode{
ID: code.ID,
ReferrerID: code.ReferrerID,
ReferralCode: code.ReferralCode,
NumberOfReferrals: code.NumberOfReferrals,
RewardAmount: Currency(code.RewardAmount),
CompanyID: code.CompanyID,
CreatedAt: code.CreatedAt.Time,
UpdatedAt: code.UpdatedAt.Time,
}
}
func ConvertDBReferralCodes(codes []dbgen.ReferralCode) []ReferralCode {
result := make([]ReferralCode, len(codes))
for i, code := range codes {
result[i] = ConvertDBReferralCode(code)
}
return result
}
func ConvertCreateUserReferral(referral CreateUserReferrals) dbgen.CreateUserReferralParams {
return dbgen.CreateUserReferralParams{
ReferredID: referral.ReferredID,
ReferralCodeID: referral.ReferralCodeID,
}
}
func ConvertDBUserReferral(referral dbgen.UserReferral) UserReferral {
return UserReferral{
ReferredID: referral.ReferredID,
ReferralCodeID: referral.ReferralCodeID,
}
}
func ConvertDBUserReferrals(referrals []dbgen.UserReferral) []UserReferral {
result := make([]UserReferral, len(referrals))
for i, referral := range referrals {
result[i] = ConvertDBUserReferral(referral)
}
return result
}
func ConvertUpdateReferralCode(referralCode UpdateReferralCode) dbgen.UpdateReferralCodeParams {
return dbgen.UpdateReferralCodeParams{
ID: referralCode.ID,
IsActive: referralCode.IsActive,
ReferralCode: referralCode.ReferralCode,
NumberOfReferrals: referralCode.NumberOfReferrals,
RewardAmount: int64(referralCode.RewardAmount),
}
}
func ConvertDBReferralStats(stats dbgen.GetReferralStatsRow) ReferralStats {
return ReferralStats{
TotalReferrals: stats.TotalReferrals,
TotalRewardEarned: Currency(stats.TotalRewardEarned),
}
}
func ConvertReferralCodeRes(referral ReferralCode) ReferralCodeRes {
return ReferralCodeRes{
ID: referral.ID,
ReferrerID: referral.ReferrerID,
ReferralCode: referral.ReferralCode,
CompanyID: referral.CompanyID,
NumberOfReferrals: referral.NumberOfReferrals,
RewardAmount: referral.RewardAmount.Float32(),
CreatedAt: referral.CreatedAt,
UpdatedAt: referral.UpdatedAt,
}
}
func ConvertReferralCodeResList(referrals []ReferralCode) []ReferralCodeRes {
result := make([]ReferralCodeRes, len(referrals))
for i, referral := range referrals {
result[i] = ConvertReferralCodeRes(referral)
}
return result
}
func ConvertReferralStatsRes(stats ReferralStats) ReferralStatsRes {
return ReferralStatsRes{
TotalReferrals: stats.TotalReferrals,
TotalRewardEarned: stats.TotalRewardEarned.Float32(),
}
}

View File

@ -8,11 +8,12 @@ const (
RoleBranchManager Role = "branch_manager"
RoleCustomer Role = "customer"
RoleCashier Role = "cashier"
RoleTransactionApprover Role = "transaction_approver"
)
func (r Role) IsValid() bool {
switch r {
case RoleSuperAdmin, RoleAdmin, RoleBranchManager, RoleCustomer, RoleCashier:
case RoleSuperAdmin, RoleAdmin, RoleBranchManager, RoleCustomer, RoleCashier, RoleTransactionApprover:
return true
default:
return false

View File

@ -23,6 +23,19 @@ 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"`
ReferralRewardAmount Currency `json:"referral_reward_amount"`
CashbackPercentage float32 `json:"cashback_percentage"`
DefaultMaxReferrals int64 `json:"default_max_referrals"`
MinimumBetAmount Currency `json:"minimum_bet_amount"`
BetDuplicateLimit int64 `json:"bet_duplicate_limit"`
SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"`
SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"`
WelcomeBonusActive bool `json:"welcome_bonus_active"`
WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"`
WelcomeBonusCap Currency `json:"welcome_bonus_cap"`
WelcomeBonusCount int64 `json:"welcome_bonus_count"`
WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"`
}
type SettingListRes struct {
@ -33,6 +46,44 @@ 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"`
ReferralRewardAmount float32 `json:"referral_reward_amount"`
CashbackPercentage float32 `json:"cashback_percentage"`
DefaultMaxReferrals int64 `json:"default_max_referrals"`
MinimumBetAmount float32 `json:"minimum_bet_amount"`
BetDuplicateLimit int64 `json:"bet_duplicate_limit"`
SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"`
SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"`
WelcomeBonusActive bool `json:"welcome_bonus_active"`
WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"`
WelcomeBonusCap float32 `json:"welcome_bonus_cap"`
WelcomeBonusCount int64 `json:"welcome_bonus_count"`
WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"`
}
func ConvertSettingListRes(settings SettingList) SettingListRes {
return SettingListRes{
SMSProvider: settings.SMSProvider,
MaxNumberOfOutcomes: settings.MaxNumberOfOutcomes,
BetAmountLimit: settings.BetAmountLimit.Float32(),
DailyTicketPerIP: settings.DailyTicketPerIP,
TotalWinningLimit: settings.TotalWinningLimit.Float32(),
AmountForBetReferral: settings.AmountForBetReferral.Float32(),
CashbackAmountCap: settings.CashbackAmountCap.Float32(),
DefaultWinningLimit: settings.DefaultWinningLimit,
ReferralRewardAmount: settings.ReferralRewardAmount.Float32(),
CashbackPercentage: settings.CashbackPercentage,
DefaultMaxReferrals: settings.DefaultMaxReferrals,
MinimumBetAmount: settings.MinimumBetAmount.Float32(),
BetDuplicateLimit: settings.BetDuplicateLimit,
SendEmailOnBetFinish: settings.SendEmailOnBetFinish,
SendSMSOnBetFinish: settings.SendSMSOnBetFinish,
WelcomeBonusActive: settings.WelcomeBonusActive,
WelcomeBonusMultiplier: settings.WelcomeBonusMultiplier,
WelcomeBonusCap: settings.WelcomeBonusCap.Float32(),
WelcomeBonusCount: settings.WelcomeBonusCount,
WelcomeBonusExpire: settings.WelcomeBonusExpire,
}
}
type SaveSettingListReq struct {
@ -43,6 +94,42 @@ 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"`
ReferralRewardAmount *float32 `json:"referral_reward_amount"`
CashbackPercentage *float32 `json:"cashback_percentage"`
DefaultMaxReferrals *int64 `json:"default_max_referrals"`
MinimumBetAmount *float32 `json:"minimum_bet_amount"`
BetDuplicateLimit *int64 `json:"bet_duplicate_limit"`
SendEmailOnBetFinish *bool `json:"send_email_on_bet_finish"`
SendSMSOnBetFinish *bool `json:"send_sms_on_bet_finish"`
WelcomeBonusActive *bool `json:"welcome_bonus_active"`
WelcomeBonusMultiplier *float32 `json:"welcome_bonus_multiplier"`
WelcomeBonusCap *float32 `json:"welcome_bonus_cap"`
WelcomeBonusCount *int64 `json:"welcome_bonus_count"`
WelcomeBonusExpire *int64 `json:"welcome_bonus_expiry"`
}
type ValidSettingList struct {
SMSProvider ValidString
MaxNumberOfOutcomes ValidInt64
BetAmountLimit ValidCurrency
DailyTicketPerIP ValidInt64
TotalWinningLimit ValidCurrency
AmountForBetReferral ValidCurrency
CashbackAmountCap ValidCurrency
DefaultWinningLimit ValidInt64
ReferralRewardAmount ValidCurrency
CashbackPercentage ValidFloat32
DefaultMaxReferrals ValidInt64
MinimumBetAmount ValidCurrency
BetDuplicateLimit ValidInt64
SendEmailOnBetFinish ValidBool
SendSMSOnBetFinish ValidBool
WelcomeBonusActive ValidBool
WelcomeBonusMultiplier ValidFloat32
WelcomeBonusCap ValidCurrency
WelcomeBonusCount ValidInt64
WelcomeBonusExpire ValidInt64
}
func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList {
@ -54,29 +141,45 @@ func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList {
TotalWinningLimit: ConvertFloat32PtrToCurrency(settings.TotalWinningLimit),
AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral),
CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap),
DefaultWinningLimit: ConvertInt64Ptr(settings.DefaultWinningLimit),
ReferralRewardAmount: ConvertFloat32PtrToCurrency(settings.ReferralRewardAmount),
CashbackPercentage: ConvertFloat32Ptr(settings.CashbackPercentage),
DefaultMaxReferrals: ConvertInt64Ptr(settings.DefaultMaxReferrals),
MinimumBetAmount: ConvertFloat32PtrToCurrency(settings.MinimumBetAmount),
BetDuplicateLimit: ConvertInt64Ptr(settings.BetDuplicateLimit),
SendEmailOnBetFinish: ConvertBoolPtr(settings.SendEmailOnBetFinish),
SendSMSOnBetFinish: ConvertBoolPtr(settings.SendSMSOnBetFinish),
WelcomeBonusActive: ConvertBoolPtr(settings.WelcomeBonusActive),
WelcomeBonusMultiplier: ConvertFloat32Ptr(settings.WelcomeBonusMultiplier),
WelcomeBonusCap: ConvertFloat32PtrToCurrency(settings.WelcomeBonusCap),
WelcomeBonusCount: ConvertInt64Ptr(settings.WelcomeBonusCount),
WelcomeBonusExpire: ConvertInt64Ptr(settings.WelcomeBonusExpire),
}
}
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{
SMSProvider: SMSProvider(vsl.SMSProvider.Value),
MaxNumberOfOutcomes: vsl.MaxNumberOfOutcomes.Value,
BetAmountLimit: Currency(vsl.BetAmountLimit.Value),
BetAmountLimit: vsl.BetAmountLimit.Value,
DailyTicketPerIP: vsl.DailyTicketPerIP.Value,
TotalWinningLimit: Currency(vsl.TotalWinningLimit.Value),
AmountForBetReferral: Currency(vsl.AmountForBetReferral.Value),
CashbackAmountCap: Currency(vsl.CashbackAmountCap.Value),
TotalWinningLimit: vsl.TotalWinningLimit.Value,
AmountForBetReferral: vsl.AmountForBetReferral.Value,
CashbackAmountCap: vsl.CashbackAmountCap.Value,
DefaultWinningLimit: vsl.DefaultWinningLimit.Value,
ReferralRewardAmount: vsl.ReferralRewardAmount.Value,
CashbackPercentage: vsl.CashbackPercentage.Value,
DefaultMaxReferrals: vsl.DefaultMaxReferrals.Value,
MinimumBetAmount: vsl.MinimumBetAmount.Value,
BetDuplicateLimit: vsl.BetDuplicateLimit.Value,
SendEmailOnBetFinish: vsl.SendEmailOnBetFinish.Value,
SendSMSOnBetFinish: vsl.SendSMSOnBetFinish.Value,
WelcomeBonusActive: vsl.WelcomeBonusActive.Value,
WelcomeBonusMultiplier: vsl.WelcomeBonusMultiplier.Value,
WelcomeBonusCap: vsl.WelcomeBonusCap.Value,
WelcomeBonusCount: vsl.WelcomeBonusCount.Value,
WelcomeBonusExpire: vsl.WelcomeBonusExpire.Value,
}
}
@ -92,6 +195,11 @@ 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,
"default_max_referrals": &vsl.DefaultMaxReferrals,
"bet_duplicate_limit": &vsl.BetDuplicateLimit,
"welcome_bonus_count": &vsl.WelcomeBonusCount,
"welcome_bonus_expiry": &vsl.WelcomeBonusExpire,
}
}
@ -101,6 +209,9 @@ func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency
"total_winnings_limit": &vsl.TotalWinningLimit,
"amount_for_bet_referral": &vsl.AmountForBetReferral,
"cashback_amount_cap": &vsl.CashbackAmountCap,
"referral_reward_amount": &vsl.ReferralRewardAmount,
"minimum_bet_amount": &vsl.MinimumBetAmount,
"welcome_bonus_cap": &vsl.WelcomeBonusCap,
}
}
@ -111,17 +222,26 @@ func (vsl *ValidSettingList) GetStringSettingsMap() map[string]*ValidString {
}
func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool {
return map[string]*ValidBool{}
return map[string]*ValidBool{
"send_email_on_bet_finish": &vsl.SendEmailOnBetFinish,
"send_sms_on_bet_finish": &vsl.SendSMSOnBetFinish,
"welcome_bonus_active": &vsl.WelcomeBonusActive,
}
}
func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 {
return map[string]*ValidFloat32{}
return map[string]*ValidFloat32{
"cashback_percentage": &vsl.CashbackPercentage,
"welcome_bonus_multiplier": &vsl.WelcomeBonusMultiplier,
}
}
func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime {
return map[string]*ValidTime{}
}
// Setting Functions
func (vsl *ValidSettingList) GetTotalSettings() int {
return len(vsl.GetInt64SettingsMap()) +
len(vsl.GetCurrencySettingsMap()) +
@ -157,57 +277,105 @@ func (vsl *ValidSettingList) GetAllValid() map[string]*bool {
return settingValid
}
func setValidSetting[T any](settings map[string]*T, searchKey string, setVal T) error {
for key, setting := range settings {
// func setValidSetting[T any](settings map[string]*T, searchKey string, searchVal string, setVal func(string) (T, error)) error {
// for key, setting := range settings {
// if key == searchKey {
// s, err := setVal(searchVal)
// if err != nil {
// return err
// }
// *setting = s
// }
// return nil
// }
// return ErrSettingNotFound
// }
func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetInt64SettingsMap() {
if key == searchKey {
*setting = setVal
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
*setting = ValidInt64{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error {
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
return setValidSetting(vsl.GetInt64SettingsMap(), searchKey, ValidInt64{Value: value, Valid: true})
}
func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetCurrencySettingsMap() {
if key == searchKey {
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
return setValidSetting(vsl.GetCurrencySettingsMap(), searchKey, ValidCurrency{Value: Currency(value), Valid: true})
*setting = ValidCurrency{Value: Currency(value), Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error {
return setValidSetting(vsl.GetStringSettingsMap(), searchKey, ValidString{Value: searchVal, Valid: true})
for key, setting := range vsl.GetStringSettingsMap() {
if key == searchKey {
*setting = ValidString{Value: searchVal, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetBoolSettingsMap() {
if key == searchKey {
value, err := strconv.ParseBool(searchVal)
if err != nil {
return err
}
return setValidSetting(vsl.GetBoolSettingsMap(), searchKey, ValidBool{Value: value, Valid: true})
*setting = ValidBool{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetFloat32SettingsMap() {
if key == searchKey {
value, err := strconv.ParseFloat(searchVal, 32)
if err != nil {
return err
}
return setValidSetting(vsl.GetFloat32SettingsMap(), searchKey, ValidFloat32{Value: float32(value), Valid: true})
*setting = ValidFloat32{Value: float32(value), Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error {
for key, setting := range vsl.GetTimeSettingsMap() {
if key == searchKey {
value, err := time.Parse(time.RFC3339, searchVal)
if err != nil {
return err
}
return setValidSetting(vsl.GetTimeSettingsMap(), searchKey, ValidTime{Value: value, Valid: true})
*setting = ValidTime{Value: value, Valid: true}
return nil
}
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error {
@ -223,9 +391,10 @@ func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) erro
for _, setter := range setters {
if err := setter(searchKey, searchVal); err != nil {
if err == ErrSettingNotFound {
// fmt.Printf("setting is not found %v \n", searchKey)
continue // not this setter, try the next
}
return fmt.Errorf("error while processing setting %q: %w", searchKey, err)
return fmt.Errorf("error while processing setting %q: %w \n", searchKey, err)
}
return nil // successfully set
}
@ -306,6 +475,7 @@ func validateSettings[T any](
var errs []string
for key, s := range settings {
if !customValidator(s) {
errs = append(errs, fmt.Sprintf("%v is invalid", key))
}
}
@ -378,6 +548,7 @@ func (vsl *ValidSettingList) ValidateAllSettings() error {
for _, validator := range validators {
if err := validator(); err != nil {
errs = append(errs, err.Error())
}
}
@ -410,12 +581,12 @@ func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, er
if err == ErrSettingNotFound {
MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
}
MongoDBLogger.Error("unknown error while fetching settings", zap.Error(err))
}
}
if err := dbSettingList.ValidateAllSettings(); err != nil {
fmt.Printf("setting validation error: %v \n", err)
MongoDBLogger.Warn("setting validation error", zap.Error(err))
MongoDBLogger.Warn("setting validation error", zap.Error(err), zap.Any("db_setting_list", dbSettingList))
return SettingList{}, err
}
@ -436,7 +607,6 @@ func ConvertDBOverrideSettingList(settings []dbgen.GetOverrideSettingsRow) (Sett
}
if err := dbSettingList.ValidateAllSettings(); err != nil {
fmt.Printf("setting validation error: %v \n", err)
MongoDBLogger.Warn("setting validation error", zap.Error(err))
return SettingList{}, err
}

View File

@ -105,3 +105,10 @@ type CreateTransfer struct {
Status string `json:"status"`
CashierID ValidInt64 `json:"cashier_id"`
}
type TransferStats struct {
TotalTransfer int64
TotalDeposits int64
TotalWithdraws int64
TotalWalletToWallet int64
}

View File

@ -23,7 +23,7 @@ type User struct {
UpdatedAt time.Time
SuspendedAt time.Time
Suspended bool
CompanyID ValidInt64 //This should be null
CompanyID ValidInt64
}
type UserFilter struct {
@ -36,7 +36,6 @@ type UserFilter struct {
CreatedAfter ValidTime
}
type RegisterUserReq struct {
FirstName string
LastName string
@ -65,6 +64,7 @@ type ResetPasswordReq struct {
Password string
Otp string
OtpMedium OtpMedium
CompanyID int64
}
type UpdateUserReq struct {
UserId int64

View File

@ -31,6 +31,7 @@ func (n *ValidInt64) UnmarshalJSON(data []byte) error {
}
v, err := strconv.ParseInt(s, 10, 64)
if err != nil {
fmt.Printf("Failed to parse the value of %v \n\n", s)
return err
}
n.Value, n.Valid = v, true
@ -42,7 +43,7 @@ func (n *ValidInt64) UnmarshalJSON(data []byte) error {
n.Value, n.Valid = v, true
return nil
}
fmt.Printf("Failed to parse the value of %v", s)
return fmt.Errorf("invalid int64 value: %s", string(data))
}

View File

@ -76,7 +76,8 @@ type CreateCustomerWallet struct {
type WalletType string
const (
CustomerWalletType WalletType = "customer_wallet"
RegularWalletType WalletType = "regular_wallet"
StaticWalletType WalletType = "static_wallet"
BranchWalletType WalletType = "branch_wallet"
CompanyWalletType WalletType = "company_wallet"
)
@ -92,18 +93,18 @@ const (
)
type DirectDeposit struct {
ID int64
CustomerID int64
WalletID int64
Wallet Wallet // Joined data
Amount Currency
BankReference string
SenderAccount string
Status DirectDepositStatus
CreatedAt time.Time
VerifiedBy *int64 // Nullable
VerificationNotes string
VerifiedAt *time.Time // Nullable
ID int64 `json:"id"`
CustomerID int64 `json:"customer_id"`
WalletID int64 `json:"wallet_id"`
Wallet Wallet `json:"wallet"`
Amount Currency `json:"amount"`
BankReference string `json:"bank_reference"`
SenderAccount string `json:"sender_account"`
Status DirectDepositStatus `json:"status"`
CreatedAt time.Time `json:"created_at"`
VerifiedBy *int64 `json:"verified_by"`
VerificationNotes string `json:"verification_notes"`
VerifiedAt *time.Time `json:"verified_at"`
}
type CreateDirectDeposit struct {

View File

@ -1,10 +1,13 @@
package helpers
import (
random "crypto/rand"
"fmt"
"math/rand/v2"
"strings"
"github.com/google/uuid"
"math/big"
"math/rand/v2"
)
func GenerateID() string {
@ -24,3 +27,34 @@ func GenerateFastCode() string {
}
return code
}
func GenerateCashoutID() (string, error) {
const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
const length int = 13
charLen := big.NewInt(int64(len(chars)))
result := make([]byte, length)
for i := 0; i < length; i++ {
index, err := random.Int(random.Reader, charLen)
if err != nil {
return "", err
}
result[i] = chars[index.Int64()]
}
return string(result), nil
}
func MaskPhone(phone string) string {
if phone == "" {
return ""
}
return phone[:4] + "**" + phone[len(phone)-2:]
}
func MaskEmail(email string) string {
if email == "" {
return ""
}
return email[:3] + "**" + email[strings.Index(email, "@"):]
}

View File

@ -10,6 +10,7 @@ import (
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"go.uber.org/zap"
)
@ -220,6 +221,46 @@ func (s *Store) UpdateStatus(ctx context.Context, id int64, status domain.Outcom
return err
}
func (s *Store) SettleWinningBet(ctx context.Context, betID int64, userID int64, amount domain.Currency, status domain.OutcomeStatus) error {
tx, err := s.conn.BeginTx(ctx, pgx.TxOptions{})
if err != nil {
return err
}
qtx := s.queries.WithTx(tx)
wallet, err := qtx.GetCustomerWallet(ctx, userID)
if err != nil {
tx.Rollback(ctx)
return err
}
// 1. Update wallet
newAmount := wallet.RegularBalance + int64(amount)
if err := qtx.UpdateBalance(ctx, dbgen.UpdateBalanceParams{
Balance: newAmount,
ID: wallet.RegularID,
}); err != nil {
tx.Rollback(ctx)
return err
}
// 2. Update bet
if err := qtx.UpdateStatus(ctx, dbgen.UpdateStatusParams{
Status: int32(status),
ID: betID,
}); err != nil {
tx.Rollback(ctx)
return err
}
// 3. Commit both together
if err := tx.Commit(ctx); err != nil {
return err
}
return nil
}
func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) {
outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, dbgen.GetBetOutcomeByEventIDParams{

View File

@ -4,27 +4,80 @@ import (
"context"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
func (s *Store) CreateBonusMultiplier(ctx context.Context, multiplier float32, balance_cap int64) error {
return s.queries.CreateBonusMultiplier(ctx, dbgen.CreateBonusMultiplierParams{
Multiplier: multiplier,
BalanceCap: balance_cap,
func (s *Store) CreateUserBonus(ctx context.Context, bonus domain.CreateBonus) (domain.UserBonus, error) {
newBonus, err := s.queries.CreateUserBonus(ctx, domain.ConvertCreateBonus(bonus))
if err != nil {
return domain.UserBonus{}, err
}
return domain.ConvertDBBonus(newBonus), nil
}
func (s *Store) GetAllUserBonuses(ctx context.Context, filter domain.BonusFilter) ([]domain.UserBonus, error) {
bonuses, err := s.queries.GetAllUserBonuses(ctx, dbgen.GetAllUserBonusesParams{
UserID: filter.UserID.ToPG(),
Offset: filter.Offset.ToPG(),
Limit: filter.Limit.ToPG(),
})
if err != nil {
return nil, err
}
return domain.ConvertDBBonuses(bonuses), nil
}
func (s *Store) GetBonusMultiplier(ctx context.Context) ([]dbgen.GetBonusMultiplierRow, error) {
return s.queries.GetBonusMultiplier(ctx)
func (s *Store) GetBonusCount(ctx context.Context, filter domain.BonusFilter) (int64, error) {
count, err := s.queries.GetBonusCount(ctx, filter.UserID.ToPG())
if err != nil {
return 0, err
}
return count, nil
}
func (s *Store) GetBonusBalanceCap(ctx context.Context) ([]dbgen.GetBonusBalanceCapRow, error) {
return s.queries.GetBonusBalanceCap(ctx)
func (s *Store) GetBonusByID(ctx context.Context, bonusID int64) (domain.UserBonus, error) {
bonus, err := s.queries.GetUserBonusByID(ctx, bonusID)
if err != nil {
return domain.UserBonus{}, err
}
return domain.ConvertDBBonus(bonus), nil
}
func (s *Store) UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32, balance_cap int64) error {
return s.queries.UpdateBonusMultiplier(ctx, dbgen.UpdateBonusMultiplierParams{
ID: id,
Multiplier: mulitplier,
BalanceCap: balance_cap,
func (s *Store) GetBonusStats(ctx context.Context, filter domain.BonusFilter) (domain.BonusStats, error) {
bonus, err := s.queries.GetBonusStats(ctx, dbgen.GetBonusStatsParams{
CompanyID: filter.CompanyID.ToPG(),
UserID: filter.UserID.ToPG(),
})
if err != nil {
return domain.BonusStats{}, err
}
return domain.ConvertDBBonusStats(bonus), nil
}
func (s *Store) UpdateUserBonus(ctx context.Context, bonusID int64, IsClaimed bool) (error) {
err := s.queries.UpdateUserBonus(ctx, dbgen.UpdateUserBonusParams{
ID: bonusID,
IsClaimed: IsClaimed,
})
if err != nil {
return err
}
return nil
}
func (s *Store) DeleteUserBonus(ctx context.Context, bonusID int64) (error) {
err := s.queries.DeleteUserBonus(ctx, bonusID)
if err != nil {
return err
}
return nil
}

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,10 +16,6 @@ func (s *Store) SaveEvent(ctx context.Context, e domain.CreateEvent) error {
return s.queries.InsertEvent(ctx, domain.ConvertCreateEvent(e))
}
func (s *Store) InsertEventSettings(ctx context.Context, eventSetting domain.CreateEventSettings) error {
return s.queries.InsertEventSettings(ctx, domain.ConvertCreateEventSettings(eventSetting))
}
func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) {
return s.queries.ListLiveEvents(ctx)
}
@ -89,6 +85,8 @@ 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(),
IsActive: filter.Active.ToPG(),
})
if err != nil {
@ -103,13 +101,70 @@ 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(),
IsActive: filter.Active.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)
@ -128,7 +183,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{
@ -176,7 +280,7 @@ func (s *Store) UpdateEventMonitored(ctx context.Context, eventID string, IsMoni
}
func (s *Store) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error {
return s.queries.UpdateEventSettings(ctx, domain.ConvertUpdateEventSettings(event))
return s.queries.SaveEventSettings(ctx, domain.ConvertUpdateEventSettings(event))
}
func (s *Store) DeleteEvent(ctx context.Context, eventID string) error {
@ -186,3 +290,13 @@ func (s *Store) DeleteEvent(ctx context.Context, eventID string) error {
}
return nil
}
func (s *Store) GetSportAndLeagueIDs(ctx context.Context, eventID string) ([]int64, error) {
sportAndLeagueIDs, err := s.queries.GetSportAndLeagueIDs(ctx, eventID)
if err != nil {
return nil, err
}
res := []int64{int64(sportAndLeagueIDs.SportID), sportAndLeagueIDs.LeagueID}
return res, err
}

View File

@ -18,6 +18,7 @@ func (s *Store) SaveLeagueSettings(ctx context.Context, leagueSettings domain.Cr
func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error) {
l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{
Query: filter.Query.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
SportID: filter.SportID.ToPG(),
Limit: pgtype.Int4{
@ -36,8 +37,10 @@ func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) (
return domain.ConvertDBBaseLeagues(l), nil
}
func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error) {
func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error) {
l, err := s.queries.GetAllLeaguesWithSettings(ctx, dbgen.GetAllLeaguesWithSettingsParams{
Query: filter.Query.ToPG(),
CompanyID: companyID,
CountryCode: filter.CountryCode.ToPG(),
SportID: filter.SportID.ToPG(),
@ -49,13 +52,27 @@ 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 nil, 0, err
}
return domain.ConvertDBLeagueWithSettings(l), nil
total, err := s.queries.GetTotalLeaguesWithSettings(ctx, dbgen.GetTotalLeaguesWithSettingsParams{
Query: filter.Query.ToPG(),
CompanyID: companyID,
CountryCode: filter.CountryCode.ToPG(),
SportID: filter.SportID.ToPG(),
IsFeatured: filter.IsFeatured.ToPG(),
IsActive: filter.IsActive.ToPG(),
})
if err != nil {
return nil, 0, err
}
return domain.ConvertDBLeagueWithSettings(l), total, 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,13 +88,51 @@ 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 {
return domain.OddMarket{}, err
}
convertedOdd, err := domain.ConvertDBOddMarket(odd)
if err != nil {
return domain.OddMarket{}, err
}
return convertedOdd, nil
}
func (s *Store) GetOddsByMarketID(ctx context.Context, marketID string, eventID string) (domain.OddMarket, error) {
@ -126,12 +164,76 @@ 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
// }
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
}
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,
CompanyID: companyID,
})
if err != nil {
return domain.OddMarketWithSettings{}, err
}
return convertedOdd, nil
// convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds)
// 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
}
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) {
@ -174,14 +276,49 @@ 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 {
return s.queries.DeleteOddsForEvent(ctx, eventID)
}
func (s *Store) SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error {
res, err := domain.ConvertCreateOddMarketSetting(odd)
if err != nil {
return nil
}
return s.queries.SaveOddSettings(ctx, res)
}

View File

@ -0,0 +1,193 @@
package repository
import (
"context"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
func convertRaffleOutcome(raffle dbgen.Raffle) domain.Raffle {
return domain.Raffle{
ID: raffle.ID,
CompanyID: raffle.CompanyID,
Name: raffle.Name,
CreatedAt: raffle.CreatedAt.Time,
ExpiresAt: raffle.ExpiresAt.Time,
Type: raffle.Type,
Status: raffle.Status,
}
}
func convertRaffleTicketOutcome(raffle dbgen.RaffleTicket) domain.RaffleTicket {
return domain.RaffleTicket{
ID: raffle.ID,
RaffleID: raffle.RaffleID,
UserID: raffle.UserID,
IsActive: raffle.IsActive.Bool,
}
}
func convertJoinedRaffleTicketOutcome(raffle dbgen.GetUserRaffleTicketsRow) domain.RaffleTicketRes {
return domain.RaffleTicketRes{
TicketID: raffle.TicketID,
UserID: raffle.UserID,
Name: raffle.Name,
Type: raffle.Type,
ExpiresAt: raffle.ExpiresAt.Time,
Status: raffle.Status,
}
}
func convertCreateRaffle(raffle domain.CreateRaffle) dbgen.CreateRaffleParams {
return dbgen.CreateRaffleParams{
CompanyID: raffle.CompanyID,
Name: raffle.Name,
ExpiresAt: pgtype.Timestamp{
Time: *raffle.ExpiresAt,
Valid: true,
},
Type: raffle.Type,
}
}
func convertRaffleStanding(raffleStanding dbgen.GetRaffleStandingRow) domain.RaffleStanding {
return domain.RaffleStanding{
UserID: raffleStanding.UserID,
RaffleID: raffleStanding.RaffleID,
FirstName: raffleStanding.FirstName,
LastName: raffleStanding.LastName,
PhoneNumber: raffleStanding.PhoneNumber.String,
Email: raffleStanding.Email.String,
TicketCount: raffleStanding.TicketCount,
}
}
func (s *Store) CreateRaffle(ctx context.Context, raffle domain.CreateRaffle) (domain.Raffle, error) {
raffleRes, err := s.queries.CreateRaffle(ctx, convertCreateRaffle(raffle))
if err != nil {
return domain.Raffle{}, err
}
return convertRaffleOutcome(raffleRes), nil
}
func (s *Store) DeleteRaffle(ctx context.Context, raffleID int32) (domain.Raffle, error) {
raffleRes, err := s.queries.DeleteRaffle(ctx, raffleID)
if err != nil {
return domain.Raffle{}, err
}
return convertRaffleOutcome(raffleRes), nil
}
func (s *Store) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]dbgen.Raffle, error) {
raffles, err := s.queries.GetRafflesOfCompany(ctx, companyID)
if err != nil {
return nil, err
}
return raffles, nil
}
func (s *Store) CreateRaffleTicket(ctx context.Context, raffleTicketParams domain.CreateRaffleTicket) (domain.RaffleTicket, error) {
raffleTicket, err := s.queries.CreateRaffleTicket(ctx, dbgen.CreateRaffleTicketParams{
RaffleID: raffleTicketParams.RaffleID,
UserID: raffleTicketParams.UserID,
})
if err != nil {
return domain.RaffleTicket{}, err
}
return convertRaffleTicketOutcome(raffleTicket), nil
}
func (s *Store) GetUserRaffleTickets(ctx context.Context, userID int32) ([]domain.RaffleTicketRes, error) {
raffleTickets, err := s.queries.GetUserRaffleTickets(ctx, userID)
if err != nil {
return nil, err
}
res := []domain.RaffleTicketRes{}
for _, raffle := range raffleTickets {
res = append(res, convertJoinedRaffleTicketOutcome(raffle))
}
return res, nil
}
func (s *Store) SuspendRaffleTicket(ctx context.Context, raffleTicketID int32) error {
return s.queries.UpdateRaffleTicketStatus(ctx, dbgen.UpdateRaffleTicketStatusParams{
ID: raffleTicketID,
IsActive: pgtype.Bool{
Bool: false,
Valid: true,
},
})
}
func (s *Store) UnSuspendRaffleTicket(ctx context.Context, raffleID int32) error {
return s.queries.UpdateRaffleTicketStatus(ctx, dbgen.UpdateRaffleTicketStatusParams{
ID: raffleID,
IsActive: pgtype.Bool{
Bool: true,
Valid: true,
},
})
}
// TODO: could also add -> suspend a specific user's raffle tickets
func (s *Store) GetRaffleStanding(ctx context.Context, raffleID, limit int32) ([]domain.RaffleStanding, error) {
raffleStanding, err := s.queries.GetRaffleStanding(ctx, dbgen.GetRaffleStandingParams{
RaffleID: raffleID,
Limit: limit,
})
if err != nil {
return nil, err
}
res := []domain.RaffleStanding{}
for _, standing := range raffleStanding {
res = append(res, convertRaffleStanding(standing))
}
return res, nil
}
func (s *Store) CreateRaffleWinner(ctx context.Context, raffleWinnerParams domain.RaffleWinnerParams) error {
_, err := s.queries.CreateRaffleWinner(ctx, dbgen.CreateRaffleWinnerParams{
RaffleID: raffleWinnerParams.RaffleID,
UserID: raffleWinnerParams.UserID,
Rank: raffleWinnerParams.Rank,
})
return err
}
func (s *Store) SetRaffleComplete(ctx context.Context, raffleID int32) error {
return s.queries.SetRaffleComplete(ctx, raffleID)
}
func (s *Store) AddSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) error {
_, err := s.queries.AddSportRaffleFilter(ctx, dbgen.AddSportRaffleFilterParams{
RaffleID: raffleID,
SportID: sportID,
LeagueID: leagueID,
})
return err
}
func (s *Store) CheckValidSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) (bool, error) {
res, err := s.queries.CheckValidSportRaffleFilter(ctx, dbgen.CheckValidSportRaffleFilterParams{
RaffleID: raffleID,
SportID: sportID,
LeagueID: leagueID,
})
if err != nil {
return false, err
}
return res, nil
}

View File

@ -2,28 +2,21 @@ package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"strconv"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
type ReferralRepository interface {
CreateReferral(ctx context.Context, referral *domain.Referral) error
GetReferralByCode(ctx context.Context, code string) (*domain.Referral, error)
UpdateReferral(ctx context.Context, referral *domain.Referral) error
GetReferralStats(ctx context.Context, userID string) (*domain.ReferralStats, error)
GetSettings(ctx context.Context) (*domain.ReferralSettings, error)
UpdateSettings(ctx context.Context, settings *domain.ReferralSettings) error
CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error
GetReferralByReferredID(ctx context.Context, referredID string) (*domain.Referral, error) // New method
GetReferralCountByID(ctx context.Context, referrerID string) (int64, error)
GetActiveReferralByReferrerID(ctx context.Context, referrerID string) (*domain.Referral, error)
UpdateUserReferalCode(ctx context.Context, codedata domain.UpdateUserReferalCode) error
CreateReferralCode(ctx context.Context, referralCode domain.CreateReferralCode) (domain.ReferralCode, error)
CreateUserReferral(ctx context.Context, referral domain.CreateUserReferrals) (domain.UserReferral, error)
GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error)
GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error)
UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error
GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error)
GetUserReferral(ctx context.Context, referredID int64) (domain.UserReferral, error)
GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error)
GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error)
}
type ReferralRepo struct {
@ -34,248 +27,159 @@ func NewReferralRepository(store *Store) ReferralRepository {
return &ReferralRepo{store: store}
}
func (r *ReferralRepo) UpdateUserReferalCode(ctx context.Context, codedata domain.UpdateUserReferalCode) error {
params := dbgen.UpdateReferralCodeParams{
ID: codedata.UserID,
ReferralCode: pgtype.Text{
String: codedata.Code,
Valid: true,
},
}
func (r *ReferralRepo) CreateReferralCode(ctx context.Context, referralCode domain.CreateReferralCode) (domain.ReferralCode, error) {
newReferralCode, err := r.store.queries.CreateReferralCode(ctx, domain.ConvertCreateReferralCode(referralCode))
return r.store.queries.UpdateReferralCode(ctx, params)
}
func (r *ReferralRepo) CreateReferral(ctx context.Context, referral *domain.Referral) error {
rewardAmount := pgtype.Numeric{}
if err := rewardAmount.Scan(strconv.Itoa(int(referral.RewardAmount))); err != nil {
return err
}
params := dbgen.CreateReferralParams{
ReferralCode: referral.ReferralCode,
ReferrerID: referral.ReferrerID,
Status: dbgen.Referralstatus(referral.Status),
RewardAmount: rewardAmount,
ExpiresAt: pgtype.Timestamptz{Time: referral.ExpiresAt, Valid: true},
}
_, err := r.store.queries.CreateReferral(ctx, params)
return err
}
func (r *ReferralRepo) GetReferralByCode(ctx context.Context, code string) (*domain.Referral, error) {
dbReferral, err := r.store.queries.GetReferralByCode(ctx, code)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
return domain.ReferralCode{}, err
}
return nil, err
}
return r.mapToDomainReferral(&dbReferral), nil
return domain.ConvertDBReferralCode(newReferralCode), nil
}
func (r *ReferralRepo) UpdateReferral(ctx context.Context, referral *domain.Referral) error {
var referredID pgtype.Text
if referral.ReferredID != nil {
referredID = pgtype.Text{String: *referral.ReferredID, Valid: true}
func (r *ReferralRepo) CreateUserReferral(ctx context.Context, referral domain.CreateUserReferrals) (domain.UserReferral, error) {
newReferral, err := r.store.queries.CreateUserReferral(ctx, domain.ConvertCreateUserReferral(referral))
if err != nil {
return domain.UserReferral{}, err
}
params := dbgen.UpdateReferralParams{
ID: referral.ID,
ReferredID: referredID,
Status: dbgen.Referralstatus(referral.Status),
}
_, err := r.store.queries.UpdateReferral(ctx, params)
return err
return domain.ConvertDBUserReferral(newReferral), nil
}
func (r *ReferralRepo) GetReferralStats(ctx context.Context, userID string) (*domain.ReferralStats, error) {
stats, err := r.store.queries.GetReferralStats(ctx, userID)
func (r *ReferralRepo) GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) {
codes, err := r.store.queries.GetReferralCodeByUser(ctx, userID)
if err != nil {
return nil, err
}
return &domain.ReferralStats{
TotalReferrals: int(stats.TotalReferrals),
CompletedReferrals: int(stats.CompletedReferrals),
TotalRewardEarned: stats.TotalRewardEarned.(float64),
PendingRewards: stats.PendingRewards.(float64),
}, nil
return domain.ConvertDBReferralCodes(codes), nil
}
func (r *ReferralRepo) GetSettings(ctx context.Context) (*domain.ReferralSettings, error) {
settings, err := r.store.queries.GetReferralSettings(ctx)
func (r *ReferralRepo) GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) {
referralCode, err := r.store.queries.GetReferralCode(ctx, code)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
return domain.ReferralCode{}, err
}
return domain.ConvertDBReferralCode(referralCode), nil
}
func (r *ReferralRepo) UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error {
err := r.store.queries.UpdateReferralCode(ctx, domain.ConvertUpdateReferralCode(referral))
if err != nil {
return err
}
return nil
}
func (r *ReferralRepo) GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) {
stats, err := r.store.queries.GetReferralStats(ctx, dbgen.GetReferralStatsParams{
ReferrerID: userID,
CompanyID: companyID,
})
if err != nil {
return domain.ReferralStats{}, err
}
return domain.ConvertDBReferralStats(stats), nil
}
func (r *ReferralRepo) GetUserReferral(ctx context.Context, referredID int64) (domain.UserReferral, error) {
dbReferral, err := r.store.queries.GetUserReferral(ctx, referredID)
if err != nil {
return domain.UserReferral{}, err
}
return domain.ConvertDBUserReferral(dbReferral), nil
}
func (r *ReferralRepo) GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) {
dbReferrals, err := r.store.queries.GetUserReferralsByCode(ctx, code)
if err != nil {
return nil, err
}
return r.mapToDomainSettings(&settings), nil
return domain.ConvertDBUserReferrals(dbReferrals), nil
}
func (r *ReferralRepo) UpdateSettings(ctx context.Context, settings *domain.ReferralSettings) error {
rewardAmount := pgtype.Numeric{}
if err := rewardAmount.Scan(settings.ReferralRewardAmount); err != nil {
return err
}
cashbackPercentage := pgtype.Numeric{}
if err := cashbackPercentage.Scan(settings.CashbackPercentage); err != nil {
return err
}
betReferralBonusPercentage := pgtype.Numeric{}
if err := betReferralBonusPercentage.Scan(settings.BetReferralBonusPercentage); err != nil {
return err
}
params := dbgen.UpdateReferralSettingsParams{
ID: settings.ID,
ReferralRewardAmount: rewardAmount,
CashbackPercentage: cashbackPercentage,
BetReferralBonusPercentage: betReferralBonusPercentage, // New field
MaxReferrals: settings.MaxReferrals,
ExpiresAfterDays: settings.ExpiresAfterDays,
UpdatedBy: settings.UpdatedBy,
}
_, err := r.store.queries.UpdateReferralSettings(ctx, params)
return err
}
func (r *ReferralRepo) CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error {
rewardAmount := pgtype.Numeric{}
if err := rewardAmount.Scan(fmt.Sprintf("%f", settings.ReferralRewardAmount)); err != nil {
return err
}
cashbackPercentage := pgtype.Numeric{}
if err := cashbackPercentage.Scan(fmt.Sprintf("%f", settings.CashbackPercentage)); err != nil {
return err
}
betReferralBonusPercentage := pgtype.Numeric{}
if err := betReferralBonusPercentage.Scan(fmt.Sprintf("%f", settings.BetReferralBonusPercentage)); err != nil {
return err
}
params := dbgen.CreateReferralSettingsParams{
ReferralRewardAmount: rewardAmount,
CashbackPercentage: cashbackPercentage,
BetReferralBonusPercentage: betReferralBonusPercentage, // New field
MaxReferrals: settings.MaxReferrals,
ExpiresAfterDays: settings.ExpiresAfterDays,
UpdatedBy: settings.UpdatedBy,
}
_, err := r.store.queries.CreateReferralSettings(ctx, params)
return err
}
func (r *ReferralRepo) GetReferralByReferredID(ctx context.Context, referredID string) (*domain.Referral, error) {
dbReferral, err := r.store.queries.GetReferralByReferredID(ctx, pgtype.Text{String: referredID, Valid: true})
func (r *ReferralRepo) GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) {
count, err := r.store.queries.GetUserReferralsCount(ctx, referrerID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
return r.mapToDomainReferral(&dbReferral), nil
}
func (r *ReferralRepo) GetReferralCountByID(ctx context.Context, referrerID string) (int64, error) {
count, err := r.store.queries.GetReferralCountByID(ctx, referrerID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return 0, nil
}
return 0, err
}
return count, nil
}
func (r *ReferralRepo) GetActiveReferralByReferrerID(ctx context.Context, referrerID string) (*domain.Referral, error) {
referral, err := r.store.queries.GetActiveReferralByReferrerID(ctx, referrerID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return &domain.Referral{}, nil
}
return &domain.Referral{}, err
}
// func (r *ReferralRepo) mapToDomainReferral(dbRef *dbgen.Referral) *domain.Referral {
// var referredID *int64
// if dbRef.ReferredID.Valid {
// referredID = &dbRef.ReferredID.Int64
// }
return r.mapToDomainReferral(&referral), nil
}
// rewardAmount := 0.0
// if dbRef.RewardAmount.Valid {
// if f8, err := dbRef.RewardAmount.Float64Value(); err == nil {
// rewardAmount = f8.Float64
// }
// }
func (r *ReferralRepo) mapToDomainReferral(dbRef *dbgen.Referral) *domain.Referral {
var referredID *string
if dbRef.ReferredID.Valid {
referredID = &dbRef.ReferredID.String
}
// cashbackAmount := 0.0
// if dbRef.CashbackAmount.Valid {
// if f8, err := dbRef.CashbackAmount.Float64Value(); err == nil {
// cashbackAmount = f8.Float64
// }
// }
rewardAmount := 0.0
if dbRef.RewardAmount.Valid {
if f8, err := dbRef.RewardAmount.Float64Value(); err == nil {
rewardAmount = f8.Float64
}
}
// return &domain.Referral{
// ID: dbRef.ID,
// ReferralCode: dbRef.ReferralCode,
// ReferrerID: dbRef.ReferrerID,
// ReferredID: referredID,
// Status: domain.ReferralStatus(dbRef.Status),
// RewardAmount: rewardAmount,
// CashbackAmount: cashbackAmount,
// CreatedAt: dbRef.CreatedAt.Time,
// UpdatedAt: dbRef.UpdatedAt.Time,
// ExpiresAt: dbRef.ExpiresAt.Time,
// }
// }
cashbackAmount := 0.0
if dbRef.CashbackAmount.Valid {
if f8, err := dbRef.CashbackAmount.Float64Value(); err == nil {
cashbackAmount = f8.Float64
}
}
// func (r *ReferralRepo) mapToDomainSettings(dbSettings *dbgen.ReferralSetting) *domain.ReferralSettings {
// rewardAmount := 0.0
// if dbSettings.ReferralRewardAmount.Valid {
// if f8, err := dbSettings.ReferralRewardAmount.Float64Value(); err == nil {
// rewardAmount = f8.Float64
// }
// }
return &domain.Referral{
ID: dbRef.ID,
ReferralCode: dbRef.ReferralCode,
ReferrerID: dbRef.ReferrerID,
ReferredID: referredID,
Status: domain.ReferralStatus(dbRef.Status),
RewardAmount: rewardAmount,
CashbackAmount: cashbackAmount,
CreatedAt: dbRef.CreatedAt.Time,
UpdatedAt: dbRef.UpdatedAt.Time,
ExpiresAt: dbRef.ExpiresAt.Time,
}
}
// cashbackPercentage := 0.0
// if dbSettings.CashbackPercentage.Valid {
// if f8, err := dbSettings.CashbackPercentage.Float64Value(); err == nil {
// cashbackPercentage = f8.Float64
// }
// }
func (r *ReferralRepo) mapToDomainSettings(dbSettings *dbgen.ReferralSetting) *domain.ReferralSettings {
rewardAmount := 0.0
if dbSettings.ReferralRewardAmount.Valid {
if f8, err := dbSettings.ReferralRewardAmount.Float64Value(); err == nil {
rewardAmount = f8.Float64
}
}
// betReferralBonusPercentage := 0.0
// if dbSettings.BetReferralBonusPercentage.Valid {
// if f8, err := dbSettings.BetReferralBonusPercentage.Float64Value(); err == nil {
// betReferralBonusPercentage = f8.Float64
// }
// }
cashbackPercentage := 0.0
if dbSettings.CashbackPercentage.Valid {
if f8, err := dbSettings.CashbackPercentage.Float64Value(); err == nil {
cashbackPercentage = f8.Float64
}
}
betReferralBonusPercentage := 0.0
if dbSettings.BetReferralBonusPercentage.Valid {
if f8, err := dbSettings.BetReferralBonusPercentage.Float64Value(); err == nil {
betReferralBonusPercentage = f8.Float64
}
}
return &domain.ReferralSettings{
ID: dbSettings.ID,
ReferralRewardAmount: rewardAmount,
CashbackPercentage: cashbackPercentage,
BetReferralBonusPercentage: betReferralBonusPercentage, // New field
MaxReferrals: dbSettings.MaxReferrals,
ExpiresAfterDays: dbSettings.ExpiresAfterDays,
UpdatedBy: dbSettings.UpdatedBy,
CreatedAt: dbSettings.CreatedAt.Time,
UpdatedAt: dbSettings.UpdatedAt.Time,
Version: dbSettings.Version,
}
}
// return &domain.ReferralSettings{
// ID: dbSettings.ID,
// ReferralRewardAmount: rewardAmount,
// CashbackPercentage: cashbackPercentage,
// BetReferralBonusPercentage: betReferralBonusPercentage, // New field
// MaxReferrals: dbSettings.MaxReferrals,
// ExpiresAfterDays: dbSettings.ExpiresAfterDays,
// UpdatedBy: dbSettings.UpdatedBy,
// CreatedAt: dbSettings.CreatedAt.Time,
// UpdatedAt: dbSettings.UpdatedAt.Time,
// Version: dbSettings.Version,
// }
// }

View File

@ -168,6 +168,6 @@ func (s *Store) DeleteCompanySetting(ctx context.Context, companyID int64, key s
Key: key,
})
}
func (s *Store) DeleteAllCompanySetting(ctx context.Context, companyID int64,) error {
func (s *Store) DeleteAllCompanySetting(ctx context.Context, companyID int64) error {
return s.queries.DeleteAllCompanySetting(ctx, companyID)
}

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

@ -148,6 +148,24 @@ func (s *Store) GetTransferByID(ctx context.Context, id int64) (domain.TransferD
return convertDBTransferDetail(transfer), nil
}
func (s *Store) GetTransferStats(ctx context.Context, walletID int64) (domain.TransferStats, error) {
stats, err := s.queries.GetTransferStats(ctx, pgtype.Int8{
Int64: walletID,
Valid: true,
})
if err != nil {
return domain.TransferStats{}, err
}
return domain.TransferStats{
TotalTransfer: stats.TotalTransfers,
TotalDeposits: stats.TotalDeposits,
TotalWithdraws: stats.TotalWithdraw,
TotalWalletToWallet: stats.TotalWalletToWallet,
}, nil
}
func (s *Store) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error {
err := s.queries.UpdateTransferVerification(ctx, dbgen.UpdateTransferVerificationParams{
ID: id,

View File

@ -9,6 +9,7 @@ import (
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
)
@ -73,7 +74,7 @@ func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int6
func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) {
user, err := s.queries.GetUserByID(ctx, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
if errors.Is(err, pgx.ErrNoRows) {
return domain.User{}, domain.ErrUserNotFound
}
return domain.User{}, err
@ -428,7 +429,7 @@ func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string, companyID d
}, nil
}
func (s *Store) UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64) error {
func (s *Store) UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64, companyId int64) error {
err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{
ID: usedOtpId,
UsedAt: pgtype.Timestamptz{
@ -449,6 +450,10 @@ func (s *Store) UpdatePassword(ctx context.Context, identifier string, password
String: identifier,
Valid: true,
},
CompanyID: pgtype.Int8{
Int64: companyId,
Valid: true,
},
})
if err != nil {
return err

View File

@ -0,0 +1,247 @@
package bet
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"go.uber.org/zap"
)
func newBetResultNotification(userID int64, level domain.NotificationLevel, channel domain.DeliveryChannel, headline, message string, metadata any) *domain.Notification {
raw, _ := json.Marshal(metadata)
return &domain.Notification{
RecipientID: userID,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
Level: level,
Reciever: domain.NotificationRecieverSideCustomer,
DeliveryChannel: channel,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
Priority: 2,
Metadata: raw,
}
}
type SendResultNotificationParam struct {
BetID int64
Status domain.OutcomeStatus
UserID int64
WinningAmount domain.Currency
Extra string
SendEmail bool
SendSMS bool
}
func (p SendResultNotificationParam) Validate() error {
if p.BetID == 0 {
return errors.New("BetID is required")
}
if p.UserID == 0 {
return errors.New("UserID is required")
}
return nil
}
func shouldSend(channel domain.DeliveryChannel, sendEmail, sendSMS bool) bool {
switch {
case channel == domain.DeliveryChannelEmail && sendEmail:
return true
case channel == domain.DeliveryChannelSMS && sendSMS:
return true
case channel == domain.DeliveryChannelInApp:
return true
default:
return false
}
}
func (s *Service) SendWinningStatusNotification(ctx context.Context, param SendResultNotificationParam) error {
if err := param.Validate(); err != nil {
return err
}
var headline string
var message string
switch param.Status {
case domain.OUTCOME_STATUS_WIN:
headline = fmt.Sprintf("Bet #%v Won!", param.BetID)
message = fmt.Sprintf(
"Congratulations! Your bet #%v has won. %.2f has been credited to your wallet.",
param.BetID,
param.WinningAmount.Float32(),
)
case domain.OUTCOME_STATUS_HALF:
headline = fmt.Sprintf("Bet #%v Half-Win", param.BetID)
message = fmt.Sprintf(
"Your bet #%v resulted in a half-win. %.2f has been credited to your wallet.",
param.BetID,
param.WinningAmount.Float32(),
)
case domain.OUTCOME_STATUS_VOID:
headline = fmt.Sprintf("Bet #%v Refunded", param.BetID)
message = fmt.Sprintf(
"Your bet #%v has been voided. %.2f has been refunded to your wallet.",
param.BetID,
param.WinningAmount.Float32(),
)
default:
return fmt.Errorf("unsupported status: %v", param.Status)
}
for _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail,
domain.DeliveryChannelSMS,
} {
if !shouldSend(channel, param.SendEmail, param.SendSMS) {
continue
}
n := newBetResultNotification(param.UserID, domain.NotificationLevelSuccess, channel, headline, message, map[string]any{
"winning_amount": param.WinningAmount.Float32(),
"status": param.Status,
"more": param.Extra,
})
if err := s.notificationSvc.SendNotification(ctx, n); err != nil {
return err
}
}
return nil
}
func (s *Service) SendLosingStatusNotification(ctx context.Context, param SendResultNotificationParam) error {
if err := param.Validate(); err != nil {
return err
}
var headline string
var message string
switch param.Status {
case domain.OUTCOME_STATUS_LOSS:
headline = fmt.Sprintf("Bet #%v Lost", param.BetID)
message = "Unfortunately, your bet did not win this time. Better luck next time!"
default:
return fmt.Errorf("unsupported status: %v", param.Status)
}
for _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail,
domain.DeliveryChannelSMS,
} {
if !shouldSend(channel, param.SendEmail, param.SendSMS) {
continue
}
n := newBetResultNotification(param.UserID, domain.NotificationLevelWarning, channel, headline, message, map[string]any{
"status": param.Status,
"more": param.Extra,
})
if err := s.notificationSvc.SendNotification(ctx, n); err != nil {
return err
}
}
return nil
}
func (s *Service) SendErrorStatusNotification(ctx context.Context, betID int64, status domain.OutcomeStatus, userID int64, extra string) error {
var headline string
var message string
switch status {
case domain.OUTCOME_STATUS_ERROR, domain.OUTCOME_STATUS_PENDING:
headline = fmt.Sprintf("Bet #%v Processing Issue", betID)
message = "We encountered a problem while processing your bet. Our team is working to resolve it as soon as possible."
default:
return fmt.Errorf("unsupported status: %v", status)
}
for _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail,
} {
n := newBetResultNotification(userID, domain.NotificationLevelError, channel, headline, message, map[string]any{
"status": status,
"more": extra,
})
if err := s.notificationSvc.SendNotification(ctx, n); err != nil {
return err
}
}
return nil
}
func (s *Service) SendAdminAlertNotification(ctx context.Context, betID int64, status domain.OutcomeStatus, extra string, companyID int64) error {
var headline string
var message string
switch status {
case domain.OUTCOME_STATUS_ERROR, domain.OUTCOME_STATUS_PENDING:
headline = fmt.Sprintf("Processing Error for Bet #%v", betID)
message = "A processing error occurred with this bet. Please review and take corrective action."
default:
return fmt.Errorf("unsupported status: %v", status)
}
super_admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{
Role: string(domain.RoleSuperAdmin),
})
if err != nil {
s.mongoLogger.Error("failed to get super_admin recipients",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{
Role: string(domain.RoleAdmin),
CompanyID: domain.ValidInt64{
Value: companyID,
Valid: true,
},
})
if err != nil {
s.mongoLogger.Error("failed to get admin recipients",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
users := append(super_admin_users, admin_users...)
for _, user := range users {
for _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail,
} {
n := newBetResultNotification(user.ID, domain.NotificationLevelError, channel, headline, message, map[string]any{
"status": status,
"more": extra,
})
if err := s.notificationSvc.SendNotification(ctx, n); err != nil {
return err
}
}
}
return nil
}

View File

@ -31,20 +31,20 @@ import (
)
var (
ErrNoEventsAvailable = errors.New("Not enough events available with the given filters")
ErrGenerateRandomOutcome = errors.New("Failed to generate any random outcome for events")
ErrOutcomesNotCompleted = errors.New("Some bet outcomes are still pending")
ErrEventHasBeenRemoved = errors.New("Event has been removed")
ErrNoEventsAvailable = errors.New("not enough events available with the given filters")
ErrGenerateRandomOutcome = errors.New("failed to generate any random outcome for events")
ErrOutcomesNotCompleted = errors.New("some bet outcomes are still pending")
ErrEventHasBeenRemoved = errors.New("event has been removed")
ErrEventHasNotEnded = errors.New("Event has not ended yet")
ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid")
ErrBranchIDRequired = errors.New("Branch ID required for this role")
ErrOutcomeLimit = errors.New("Too many outcomes on a single bet")
ErrTotalBalanceNotEnough = errors.New("Total Wallet balance is insufficient to create bet")
ErrEventHasNotEnded = errors.New("event has not ended yet")
ErrRawOddInvalid = errors.New("prematch Raw Odd is Invalid")
ErrBranchIDRequired = errors.New("branch ID required for this role")
ErrOutcomeLimit = errors.New("too many outcomes on a single bet")
ErrTotalBalanceNotEnough = errors.New("total Wallet balance is insufficient to create bet")
ErrInvalidAmount = errors.New("Invalid amount")
ErrBetAmountTooHigh = errors.New("Cannot create a bet with an amount above limit")
ErrBetWinningTooHigh = errors.New("Total Winnings over set limit")
ErrInvalidAmount = errors.New("invalid amount")
ErrBetAmountTooHigh = errors.New("cannot create a bet with an amount above limit")
ErrBetWinningTooHigh = errors.New("total Winnings over set limit")
)
type Service struct {
@ -97,10 +97,6 @@ func (s *Service) GenerateCashoutID() (string, error) {
for i := 0; i < length; i++ {
index, err := rand.Int(rand.Reader, charLen)
if err != nil {
s.mongoLogger.Error("failed to generate random index for cashout ID",
zap.Int("position", i),
zap.Error(err),
)
return "", err
}
result[i] = chars[index.Int64()]
@ -221,7 +217,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
if err != nil {
return domain.CreateBetRes{}, err
}
if req.Amount < 1 {
if req.Amount < settingsList.MinimumBetAmount.Float32() {
return domain.CreateBetRes{}, ErrInvalidAmount
}
@ -283,8 +279,9 @@ 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")
if role == domain.RoleCustomer && count >= settingsList.BetDuplicateLimit {
return domain.CreateBetRes{}, fmt.Errorf("max user limit for duplicate bet")
}
fastCode := helpers.GenerateFastCode()
@ -340,6 +337,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 +345,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),
)
}
@ -382,7 +382,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
zap.String("role", string(role)),
zap.Int64("user_id", userID),
)
return domain.CreateBetRes{}, fmt.Errorf("Unknown Role Type")
return domain.CreateBetRes{}, fmt.Errorf("unknown role type")
}
bet, err := s.CreateBet(ctx, newBet)
@ -583,25 +583,21 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
var newOdds []domain.CreateBetOutcome
var totalOdds float32 = 1
markets, err := s.prematchSvc.GetOddsByEventID(ctx, eventID, domain.OddMarketWithEventFilter{})
if err != nil {
s.logger.Error("failed to get odds for event", "event id", eventID, "error", err)
s.mongoLogger.Error("failed to get odds for event",
eventLogger := s.mongoLogger.With(
zap.String("eventID", eventID),
zap.Int32("sportID", sportID),
zap.String("homeTeam", HomeTeam),
zap.String("awayTeam", AwayTeam),
zap.Error(err))
)
markets, err := s.prematchSvc.GetOddsByEventID(ctx, eventID, domain.OddMarketWithEventFilter{})
if err != nil {
eventLogger.Error("failed to get odds for event", zap.Error(err))
return nil, 0, err
}
if len(markets) == 0 {
s.logger.Error("empty odds for event", "event id", eventID)
s.mongoLogger.Warn("empty odds for event",
zap.String("eventID", eventID),
zap.Int32("sportID", sportID),
zap.String("homeTeam", HomeTeam),
zap.String("awayTeam", AwayTeam))
eventLogger.Warn("empty odds for event")
return nil, 0, fmt.Errorf("empty odds or event %v", eventID)
}
@ -630,19 +626,13 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
err = json.Unmarshal(rawBytes, &selectedOdd)
if err != nil {
s.logger.Error("Failed to unmarshal raw odd", "error", err)
s.mongoLogger.Warn("Failed to unmarshal raw odd",
zap.String("eventID", eventID),
zap.Int32("sportID", sportID),
zap.Error(err))
eventLogger.Warn("Failed to unmarshal raw odd", zap.Error(err))
continue
}
parsedOdd, err := strconv.ParseFloat(selectedOdd.Odds, 32)
if err != nil {
s.logger.Error("Failed to parse odd", "error", err)
s.mongoLogger.Warn("Failed to parse odd",
zap.String("eventID", eventID),
eventLogger.Warn("Failed to parse odd",
zap.String("oddValue", selectedOdd.Odds),
zap.Error(err))
continue
@ -650,17 +640,13 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
eventIDInt, err := strconv.ParseInt(eventID, 10, 64)
if err != nil {
s.logger.Error("Failed to parse eventID", "error", err)
s.mongoLogger.Warn("Failed to parse eventID",
zap.String("eventID", eventID),
zap.Error(err))
eventLogger.Warn("Failed to parse eventID", zap.Error(err))
continue
}
oddID, err := strconv.ParseInt(selectedOdd.ID, 10, 64)
if err != nil {
s.logger.Error("Failed to parse oddID", "error", err)
s.mongoLogger.Warn("Failed to parse oddID",
eventLogger.Warn("Failed to parse oddID",
zap.String("oddID", selectedOdd.ID),
zap.Error(err))
continue
@ -668,8 +654,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
marketID, err := strconv.ParseInt(market.MarketID, 10, 64)
if err != nil {
s.logger.Error("Failed to parse marketID", "error", err)
s.mongoLogger.Warn("Failed to parse marketID",
eventLogger.Warn("Failed to parse marketID",
zap.String("marketID", market.MarketID),
zap.Error(err))
continue
@ -696,22 +681,12 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
}
if len(newOdds) == 0 {
s.logger.Error("Bet Outcomes is empty for market", "selectedMarkets", len(selectedMarkets))
s.mongoLogger.Error("Bet Outcomes is empty for market",
zap.String("eventID", eventID),
zap.Int32("sportID", sportID),
zap.String("homeTeam", HomeTeam),
zap.String("awayTeam", AwayTeam),
zap.Int("selectedMarkets", len(selectedMarkets)))
eventLogger.Error("Bet Outcomes is empty for market", zap.Int("selectedMarkets", len(selectedMarkets)))
return nil, 0, ErrGenerateRandomOutcome
}
// ✅ Final success log (optional)
s.mongoLogger.Info("Random bet outcomes generated successfully",
zap.String("eventID", eventID),
zap.Int32("sportID", sportID),
zap.Int("numOutcomes", len(newOdds)),
zap.Float32("totalOdds", totalOdds))
eventLogger.Info("Random bet outcomes generated successfully", zap.Int("numOutcomes", len(newOdds)), zap.Float32("totalOdds", totalOdds))
return newOdds, totalOdds, nil
}
@ -719,7 +694,15 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyID int64, leagueID domain.ValidInt64, sportID domain.ValidInt32, firstStartTime, lastStartTime domain.ValidTime) (domain.CreateBetRes, error) {
// Get a unexpired event id
randomBetLogger := s.mongoLogger.With(
zap.Int64("userID", userID),
zap.Int64("branchID", branchID),
zap.Int64("companyID", companyID),
zap.Any("leagueID", leagueID),
zap.Any("sportID", sportID),
zap.Any("firstStartTime", firstStartTime),
zap.Any("lastStartTime", lastStartTime),
)
events, _, err := s.eventSvc.GetPaginatedUpcomingEvents(ctx,
domain.EventFilter{
SportID: sportID,
@ -729,17 +712,12 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyI
})
if err != nil {
s.mongoLogger.Error("failed to get paginated upcoming events",
zap.Int64("userID", userID),
zap.Int64("branchID", branchID),
zap.Error(err))
randomBetLogger.Error("failed to get paginated upcoming events", zap.Error(err))
return domain.CreateBetRes{}, err
}
if len(events) == 0 {
s.mongoLogger.Warn("no events available for random bet",
zap.Int64("userID", userID),
zap.Int64("branchID", branchID))
randomBetLogger.Warn("no events available for random bet")
return domain.CreateBetRes{}, ErrNoEventsAvailable
}
@ -765,12 +743,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyI
newOdds, total, err := s.GenerateRandomBetOutcomes(ctx, event.ID, event.SportID, event.HomeTeam, event.AwayTeam, event.StartTime, numMarketsPerBet)
if err != nil {
s.logger.Error("failed to generate random bet outcome", "event id", event.ID, "error", err)
s.mongoLogger.Error("failed to generate random bet outcome",
zap.Int64("userID", userID),
zap.Int64("branchID", branchID),
zap.String("eventID", event.ID),
zap.String("error", fmt.Sprintf("%v", err)))
s.mongoLogger.Error("failed to generate random bet outcome", zap.String("eventID", event.ID), zap.Error(err))
continue
}
@ -779,10 +752,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyI
}
if len(randomOdds) == 0 {
s.logger.Error("Failed to generate random any outcomes for all events")
s.mongoLogger.Error("Failed to generate random any outcomes for all events",
zap.Int64("userID", userID),
zap.Int64("branchID", branchID))
randomBetLogger.Error("Failed to generate random any outcomes for all events")
return domain.CreateBetRes{}, ErrGenerateRandomOutcome
}
@ -790,20 +760,13 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyI
outcomesHash, err := generateOutcomeHash(randomOdds)
if err != nil {
s.mongoLogger.Error("failed to generate outcome hash",
zap.Int64("user_id", userID),
zap.Error(err),
)
randomBetLogger.Error("failed to generate outcome hash", zap.Error(err))
return domain.CreateBetRes{}, err
}
count, err := s.GetBetCountByUserID(ctx, userID, outcomesHash)
if err != nil {
s.mongoLogger.Error("failed to get bet count",
zap.Int64("user_id", userID),
zap.String("outcome_hash", outcomesHash),
zap.Error(err),
)
randomBetLogger.Error("failed to get bet count", zap.String("outcome_hash", outcomesHash), zap.Error(err))
return domain.CreateBetRes{}, err
}
@ -825,10 +788,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyI
bet, err := s.CreateBet(ctx, newBet)
if err != nil {
s.mongoLogger.Error("Failed to create a new random bet",
zap.Int64("userID", userID),
zap.Int64("branchID", branchID),
zap.String("bet", fmt.Sprintf("%+v", newBet)))
randomBetLogger.Error("Failed to create a new random bet", zap.Error(err))
return domain.CreateBetRes{}, err
}
@ -838,19 +798,13 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyI
rows, err := s.betStore.CreateBetOutcome(ctx, randomOdds)
if err != nil {
s.mongoLogger.Error("Failed to create a new random bet outcome",
zap.Int64("userID", userID),
zap.Int64("branchID", branchID),
zap.String("randomOdds", fmt.Sprintf("%+v", randomOdds)))
randomBetLogger.Error("Failed to create a new random bet outcome", zap.Any("randomOdds", randomOdds))
return domain.CreateBetRes{}, err
}
res := domain.ConvertCreateBetRes(bet, rows)
s.mongoLogger.Info("Random bets placed successfully",
zap.Int64("userID", userID),
zap.Int64("branchID", branchID),
zap.String("response", fmt.Sprintf("%+v", res)))
randomBetLogger.Info("Random bets placed successfully")
return res, nil
}
@ -897,53 +851,73 @@ func (s *Service) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) e
return s.betStore.UpdateCashOut(ctx, id, cashedOut)
}
func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error {
bet, err := s.GetBetByID(ctx, id)
if err != nil {
s.mongoLogger.Error("failed to update bet status: invalid bet ID",
zap.Int64("bet_id", id),
zap.Error(err),
func (s *Service) UpdateStatus(ctx context.Context, betId int64, status domain.OutcomeStatus) error {
updateLogger := s.mongoLogger.With(
zap.Int64("bet_id", betId),
zap.String("status", status.String()),
)
bet, err := s.GetBetByID(ctx, betId)
if err != nil {
updateLogger.Error("failed to update bet status: invalid bet ID", zap.Error(err))
return err
}
settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, bet.CompanyID)
if err != nil {
updateLogger.Error("failed to get settings", zap.Error(err))
return err
}
if status == domain.OUTCOME_STATUS_ERROR || status == domain.OUTCOME_STATUS_PENDING {
s.SendAdminErrorAlertNotification(ctx, status, "")
s.SendErrorStatusNotification(ctx, status, bet.UserID, "")
s.mongoLogger.Error("Bet Status is error",
zap.Int64("bet_id", id),
zap.Error(err),
)
return s.betStore.UpdateStatus(ctx, id, status)
}
if bet.IsShopBet {
return s.betStore.UpdateStatus(ctx, id, status)
}
customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, id)
if err != nil {
s.mongoLogger.Error("failed to get customer wallet",
zap.Int64("bet_id", id),
zap.Error(err),
)
if err := s.SendAdminAlertNotification(ctx, betId, status, "", bet.CompanyID); err != nil {
updateLogger.Error("failed to send admin notification", zap.Error(err))
return err
}
if err := s.SendErrorStatusNotification(ctx, betId, status, bet.UserID, ""); err != nil {
updateLogger.Error("failed to send error notification to user", zap.Error(err))
return err
}
updateLogger.Error("bet entered error/pending state")
return s.betStore.UpdateStatus(ctx, betId, status)
}
if bet.IsShopBet {
return s.betStore.UpdateStatus(ctx, betId, status)
}
// After this point the bet is known to be a online customer bet
customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, bet.UserID)
if err != nil {
updateLogger.Error("failed to get customer wallet", zap.Error(err))
return err
}
resultNotification := SendResultNotificationParam{
BetID: betId,
Status: status,
UserID: bet.UserID,
SendEmail: settingsList.SendEmailOnBetFinish,
SendSMS: settingsList.SendSMSOnBetFinish,
}
var amount domain.Currency
switch status {
case domain.OUTCOME_STATUS_LOSS:
s.SendLosingStatusNotification(ctx, status, bet.UserID, "")
return s.betStore.UpdateStatus(ctx, id, status)
err := s.SendLosingStatusNotification(ctx, resultNotification)
if err != nil {
updateLogger.Error("failed to send notification", zap.Error(err))
return err
}
return s.betStore.UpdateStatus(ctx, betId, status)
case domain.OUTCOME_STATUS_WIN:
amount = domain.CalculateWinnings(bet.Amount, bet.TotalOdds)
s.SendWinningStatusNotification(ctx, status, bet.UserID, amount, "")
case domain.OUTCOME_STATUS_HALF:
amount = domain.CalculateWinnings(bet.Amount, bet.TotalOdds) / 2
s.SendWinningStatusNotification(ctx, status, bet.UserID, amount, "")
case domain.OUTCOME_STATUS_VOID:
amount = bet.Amount
s.SendWinningStatusNotification(ctx, status, bet.UserID, amount, "")
default:
updateLogger.Error("invalid outcome status")
return fmt.Errorf("invalid outcome status")
}
@ -951,7 +925,7 @@ func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.Outc
domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet by system for winning a bet", amount.Float32()))
if err != nil {
s.mongoLogger.Error("failed to add winnings to wallet",
updateLogger.Error("failed to add winnings to wallet",
zap.Int64("wallet_id", customerWallet.RegularID),
zap.Float32("amount", float32(amount)),
zap.Error(err),
@ -959,231 +933,23 @@ func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.Outc
return err
}
return s.betStore.UpdateStatus(ctx, id, status)
}
func (s *Service) SendWinningStatusNotification(ctx context.Context, status domain.OutcomeStatus, userID int64, winningAmount domain.Currency, extra string) error {
var headline string
var message string
switch status {
case domain.OUTCOME_STATUS_WIN:
headline = "You Bet Has Won!"
message = fmt.Sprintf(
"You have been awarded %.2f",
winningAmount.Float32(),
)
case domain.OUTCOME_STATUS_HALF:
headline = "You have a half win"
message = fmt.Sprintf(
"You have been awarded %.2f",
winningAmount.Float32(),
)
case domain.OUTCOME_STATUS_VOID:
headline = "Your bet has been refunded"
message = fmt.Sprintf(
"You have been awarded %.2f",
winningAmount.Float32(),
)
}
betNotification := &domain.Notification{
RecipientID: userID,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
Level: domain.NotificationLevelSuccess,
Reciever: domain.NotificationRecieverSideCustomer,
DeliveryChannel: domain.DeliveryChannelInApp,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
Priority: 2,
Metadata: fmt.Appendf(nil, `{
"winning_amount":%.2f,
"status":%v
"more": %v
}`, winningAmount.Float32(), status, extra),
}
if err := s.notificationSvc.SendNotification(ctx, betNotification); err != nil {
return err
}
betNotification.DeliveryChannel = domain.DeliveryChannelEmail
if err := s.notificationSvc.SendNotification(ctx, betNotification); err != nil {
return err
}
return nil
}
func (s *Service) SendLosingStatusNotification(ctx context.Context, status domain.OutcomeStatus, userID int64, extra string) error {
var headline string
var message string
switch status {
case domain.OUTCOME_STATUS_LOSS:
headline = "Your bet has lost"
message = "Better luck next time"
}
betNotification := &domain.Notification{
RecipientID: userID,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
Level: domain.NotificationLevelSuccess,
Reciever: domain.NotificationRecieverSideCustomer,
DeliveryChannel: domain.DeliveryChannelInApp,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
Priority: 2,
Metadata: fmt.Appendf(nil, `{
"status":%v
"more": %v
}`, status, extra),
}
if err := s.notificationSvc.SendNotification(ctx, betNotification); err != nil {
return err
}
betNotification.DeliveryChannel = domain.DeliveryChannelEmail
if err := s.notificationSvc.SendNotification(ctx, betNotification); err != nil {
return err
}
return nil
}
func (s *Service) SendErrorStatusNotification(ctx context.Context, status domain.OutcomeStatus, userID int64, extra string) error {
var headline string
var message string
switch status {
case domain.OUTCOME_STATUS_ERROR, domain.OUTCOME_STATUS_PENDING:
headline = "There was an error with your bet"
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,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
Level: domain.NotificationLevelSuccess,
Reciever: domain.NotificationRecieverSideCustomer,
DeliveryChannel: domain.DeliveryChannelInApp,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
Priority: 1,
ErrorSeverity: &errorSeverityLevel,
Metadata: fmt.Appendf(nil, `{
"status":%v
"more": %v
}`, status, extra),
}
if err := s.notificationSvc.SendNotification(ctx, betNotification); err != nil {
return err
}
betNotification.DeliveryChannel = domain.DeliveryChannelEmail
if err := s.notificationSvc.SendNotification(ctx, betNotification); err != nil {
return err
}
return nil
}
func (s *Service) SendAdminErrorAlertNotification(ctx context.Context, status domain.OutcomeStatus, extra string) error {
var headline string
var message string
switch status {
case domain.OUTCOME_STATUS_ERROR, domain.OUTCOME_STATUS_PENDING:
headline = "There was an error processing bet"
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,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
Level: domain.NotificationLevelSuccess,
Reciever: domain.NotificationRecieverSideCustomer,
DeliveryChannel: domain.DeliveryChannelEmail,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
Priority: 2,
Metadata: fmt.Appendf(nil, `{
"status":%v
"more": %v
}`, status, extra),
}
super_admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{
Role: string(domain.RoleSuperAdmin),
})
if err != nil {
s.mongoLogger.Error("failed to get super_admin recipients",
if err := s.betStore.UpdateStatus(ctx, betId, status); err != nil {
updateLogger.Error("failed to update bet status",
zap.String("status", status.String()),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{
Role: string(domain.RoleAdmin),
})
resultNotification.WinningAmount = amount
if err := s.SendWinningStatusNotification(ctx, resultNotification); err != nil {
if err != nil {
s.mongoLogger.Error("failed to get admin recipients",
updateLogger.Error("failed to send winning notification",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
users := append(super_admin_users, admin_users...)
for _, user := range users {
betNotification.RecipientID = user.ID
if err := s.notificationSvc.SendNotification(ctx, betNotification); err != nil {
s.mongoLogger.Error("failed to send admin notification",
zap.Int64("admin_id", user.ID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
betNotification.DeliveryChannel = domain.DeliveryChannelEmail
if err := s.notificationSvc.SendNotification(ctx, betNotification); err != nil {
s.mongoLogger.Error("failed to send email admin notification",
zap.Int64("admin_id", user.ID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
}
return nil
}
@ -1322,7 +1088,6 @@ func (s *Service) ProcessBetCashback(ctx context.Context) error {
return err
}
for _, bet := range bets {
shouldProcess := true
loseCount := 0
@ -1365,6 +1130,14 @@ func (s *Service) ProcessBetCashback(ctx context.Context) error {
}
settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, bet.CompanyID)
if err != nil {
s.mongoLogger.Error("Failed to get settings",
zap.Int64("userID", bet.UserID),
zap.Error(err))
return err
}
cashbackAmount := math.Min(float64(settingsList.CashbackAmountCap.Float32()), float64(calculateCashbackAmount(bet.Amount.Float32(), bet.TotalOdds)))
_, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, domain.ToCurrency(float32(cashbackAmount)), domain.ValidInt64{}, domain.TRANSFER_DIRECT,

View File

@ -0,0 +1,83 @@
package bonus
import (
"context"
"encoding/json"
"fmt"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type SendBonusNotificationParam struct {
BonusID int64
UserID int64
Type domain.BonusType
Amount domain.Currency
SendEmail bool
SendSMS bool
}
func shouldSend(channel domain.DeliveryChannel, sendEmail, sendSMS bool) bool {
switch {
case channel == domain.DeliveryChannelEmail && sendEmail:
return true
case channel == domain.DeliveryChannelSMS && sendSMS:
return true
case channel == domain.DeliveryChannelInApp:
return true
default:
return false
}
}
func (s *Service) SendBonusNotification(ctx context.Context, param SendBonusNotificationParam) error {
var headline string
var message string
switch param.Type {
case domain.WelcomeBonus:
headline = "You've been awarded a welcome bonus!"
message = fmt.Sprintf(
"Congratulations! A you've been given %.2f as a welcome bonus for you to bet on.",
param.Amount,
)
default:
return fmt.Errorf("unsupported bonus type: %v", param.Type)
}
for _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail,
domain.DeliveryChannelSMS,
} {
if !shouldSend(channel, param.SendEmail, param.SendSMS) {
continue
}
raw, _ := json.Marshal(map[string]any{
"bonus_id": param.BonusID,
"type": param.Type,
})
n := &domain.Notification{
RecipientID: param.UserID,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BONUS_AWARDED,
Level: domain.NotificationLevelSuccess,
Reciever: domain.NotificationRecieverSideCustomer,
DeliveryChannel: channel,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
Priority: 2,
Metadata: raw,
}
if err := s.notificationSvc.SendNotification(ctx, n); err != nil {
return err
}
}
return nil
}

View File

@ -3,12 +3,15 @@ package bonus
import (
"context"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type BonusStore interface {
CreateBonusMultiplier(ctx context.Context, multiplier float32, balance_cap int64) error
GetBonusMultiplier(ctx context.Context) ([]dbgen.GetBonusMultiplierRow, error)
GetBonusBalanceCap(ctx context.Context) ([]dbgen.GetBonusBalanceCapRow, error)
UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32, balance_cap int64) error
CreateUserBonus(ctx context.Context, bonus domain.CreateBonus) (domain.UserBonus, error)
GetAllUserBonuses(ctx context.Context, filter domain.BonusFilter) ([]domain.UserBonus, error)
GetBonusCount(ctx context.Context, filter domain.BonusFilter) (int64, error)
GetBonusByID(ctx context.Context, bonusID int64) (domain.UserBonus, error)
GetBonusStats(ctx context.Context, filter domain.BonusFilter) (domain.BonusStats, error)
UpdateUserBonus(ctx context.Context, bonusID int64, IsClaimed bool) error
DeleteUserBonus(ctx context.Context, bonusID int64) error
}

View File

@ -2,32 +2,162 @@ package bonus
import (
"context"
"errors"
"fmt"
"math"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"go.uber.org/zap"
)
type Service struct {
bonusStore BonusStore
walletSvc *wallet.Service
settingSvc *settings.Service
notificationSvc *notificationservice.Service
mongoLogger *zap.Logger
}
func NewService(bonusStore BonusStore) *Service {
func NewService(bonusStore BonusStore, walletSvc *wallet.Service, settingSvc *settings.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) *Service {
return &Service{
bonusStore: bonusStore,
walletSvc: walletSvc,
settingSvc: settingSvc,
notificationSvc: notificationSvc,
mongoLogger: mongoLogger,
}
}
func (s *Service) CreateBonusMultiplier(ctx context.Context, multiplier float32, balance_cap int64) error {
return s.bonusStore.CreateBonusMultiplier(ctx, multiplier, balance_cap)
var (
ErrWelcomeBonusNotActive = errors.New("welcome bonus is not active")
ErrWelcomeBonusCountReached = errors.New("welcome bonus max deposit count reached")
)
func (s *Service) CreateWelcomeBonus(ctx context.Context, amount domain.Currency, companyID int64, userID int64) error {
settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID)
if err != nil {
s.mongoLogger.Error("Failed to get settings",
zap.Int64("companyID", companyID),
zap.Error(err))
return err
}
if !settingsList.WelcomeBonusActive {
return ErrWelcomeBonusNotActive
}
wallet, err := s.walletSvc.GetCustomerWallet(ctx, userID)
if err != nil {
return err
}
stats, err := s.walletSvc.GetTransferStats(ctx, wallet.ID)
if err != nil {
return err
}
if stats.TotalDeposits > settingsList.WelcomeBonusCount {
return ErrWelcomeBonusCountReached
}
newBalance := math.Min(float64(amount)*float64(settingsList.WelcomeBonusMultiplier), float64(settingsList.WelcomeBonusCap))
bonus, err := s.CreateUserBonus(ctx, domain.CreateBonus{
Name: "Welcome Bonus",
Description: fmt.Sprintf("Awarded for deposit number (%v / %v)", stats.TotalDeposits, settingsList.WelcomeBonusCount),
UserID: userID,
Type: domain.WelcomeBonus,
RewardAmount: domain.Currency(newBalance),
ExpiresAt: time.Now().Add(time.Duration(settingsList.WelcomeBonusExpire) * 24 * time.Hour),
})
if err != nil {
return err
}
err = s.SendBonusNotification(ctx, SendBonusNotificationParam{
BonusID: bonus.ID,
UserID: userID,
Type: domain.DepositBonus,
Amount: domain.Currency(newBalance),
SendEmail: true,
SendSMS: false,
})
if err != nil {
return err
}
return nil
}
func (s *Service) GetBonusMultiplier(ctx context.Context) ([]dbgen.GetBonusMultiplierRow, error) {
return s.bonusStore.GetBonusMultiplier(ctx)
var (
ErrBonusIsAlreadyClaimed = errors.New("bonus is already claimed")
ErrBonusUserIDNotMatch = errors.New("bonus user id is not a match")
)
func (s *Service) ProcessBonusClaim(ctx context.Context, bonusID, userID int64) error {
bonus, err := s.GetBonusByID(ctx, bonusID)
if err != nil {
return err
}
if bonus.UserID != userID {
}
if bonus.IsClaimed {
return ErrBonusIsAlreadyClaimed
}
wallet, err := s.walletSvc.GetCustomerWallet(ctx, bonus.UserID)
if err != nil {
return err
}
_, err = s.walletSvc.AddToWallet(
ctx, wallet.StaticID, bonus.RewardAmount,
domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %v to bonus wallet due to %v", bonus.RewardAmount, bonus.Type),
)
if err != nil {
return err
}
if err := s.UpdateUserBonus(ctx, bonusID, true); err != nil {
return err
}
return nil
}
func (s *Service) GetBonusBalanceCap(ctx context.Context) ([]dbgen.GetBonusBalanceCapRow, error) {
return s.bonusStore.GetBonusBalanceCap(ctx)
func (s *Service) CreateUserBonus(ctx context.Context, bonus domain.CreateBonus) (domain.UserBonus, error) {
return s.bonusStore.CreateUserBonus(ctx, bonus)
}
func (s *Service) GetAllUserBonuses(ctx context.Context, filter domain.BonusFilter) ([]domain.UserBonus, error) {
return s.bonusStore.GetAllUserBonuses(ctx, filter)
}
func (s *Service) UpdateBonusMultiplier(ctx context.Context, id int64, mulitplier float32, balance_cap int64) error {
return s.bonusStore.UpdateBonusMultiplier(ctx, id, mulitplier, balance_cap)
func (s *Service) GetBonusCount(ctx context.Context, filter domain.BonusFilter) (int64, error) {
return s.bonusStore.GetBonusCount(ctx, filter)
}
func (s *Service) GetBonusByID(ctx context.Context, bonusID int64) (domain.UserBonus, error) {
return s.bonusStore.GetBonusByID(ctx, bonusID)
}
func (s *Service) GetBonusStats(ctx context.Context, filter domain.BonusFilter) (domain.BonusStats, error) {
return s.bonusStore.GetBonusStats(ctx, filter)
}
func (s *Service) UpdateUserBonus(ctx context.Context, bonusID int64, IsClaimed bool) error {
return s.bonusStore.UpdateUserBonus(ctx, bonusID, IsClaimed)
}
func (s *Service) DeleteUserBonus(ctx context.Context, bonusID int64) error {
return s.bonusStore.DeleteUserBonus(ctx, bonusID)
}

View File

@ -21,4 +21,5 @@ type Service interface {
GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error)
GetEventWithSettingByID(ctx context.Context, ID string, companyID int64) (domain.EventWithSettings, error)
UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error
GetSportAndLeagueIDs(ctx context.Context, eventID string) ([]int64, error)
}

View File

@ -12,8 +12,11 @@ import (
"sync"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
"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,14 +24,18 @@ import (
type service struct {
token string
store *repository.Store
settingSvc settings.Service
mongoLogger *zap.Logger
cfg *config.Config
}
func New(token string, store *repository.Store, mongoLogger *zap.Logger) Service {
func New(token string, store *repository.Store, settingSvc settings.Service, mongoLogger *zap.Logger, cfg *config.Config) Service {
return &service{
token: token,
store: store,
settingSvc: settingSvc,
mongoLogger: mongoLogger,
cfg: cfg,
}
}
@ -206,22 +213,39 @@ 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
}
var pageLimit int
var sportIDs []int
// Restricting the page to 1 on development, which drastically reduces the amount of events that is fetched
if s.cfg.Env == "development" {
pageLimit = 1
sportIDs = []int{1}
} else {
pageLimit = 200
sportIDs = []int{1, 18, 17, 3, 83, 15, 12, 19, 8, 16, 91}
}
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 +327,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 +361,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 +370,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 +403,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
}
@ -482,6 +508,11 @@ func (s *service) GetEventsWithSettings(ctx context.Context, companyID int64, fi
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 {
return s.store.UpdateEventSettings(ctx, event)
}
func (s *service) GetSportAndLeagueIDs(ctx context.Context, eventID string) ([]int64, error) {
return s.store.GetSportAndLeagueIDs(ctx, eventID)
}

View File

@ -10,7 +10,7 @@ type Service interface {
SaveLeague(ctx context.Context, league domain.CreateLeague) error
SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error
GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error)
GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error)
GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error)
CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error)
UpdateLeague(ctx context.Context, league domain.UpdateLeague) error
}

View File

@ -29,7 +29,7 @@ func (s *service) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter)
return s.store.GetAllLeagues(ctx, filter)
}
func (s *service) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error) {
func (s *service) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error) {
return s.store.GetAllLeaguesByCompany(ctx, companyID, filter)
}

View File

@ -3,10 +3,9 @@ package messenger
import (
"context"
"github.com/resend/resend-go/v2"
)
func (s *Service) SendEmail(ctx context.Context, receiverEmail, message string, subject string) error {
func (s *Service) SendEmail(ctx context.Context, receiverEmail, message string, messageHTML string, subject string) error {
apiKey := s.config.ResendApiKey
client := resend.NewClient(apiKey)
formattedSenderEmail := "FortuneBets <" + s.config.ResendSenderEmail + ">"
@ -15,6 +14,7 @@ func (s *Service) SendEmail(ctx context.Context, receiverEmail, message string,
To: []string{receiverEmail},
Subject: subject,
Text: message,
Html: messageHTML,
}
_, err := client.Emails.Send(params)

Some files were not shown because too many files have changed in this diff Show More