Enetpulse fixture and preodds

This commit is contained in:
Yared Yemane 2025-10-15 16:41:45 +03:00
commit e429810ebc
106 changed files with 10430 additions and 2505 deletions

View File

@ -41,7 +41,6 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions"
issuereporting "github.com/SamuelTariku/FortuneBet-Backend/internal/services/issue_reporting" issuereporting "github.com/SamuelTariku/FortuneBet-Backend/internal/services/issue_reporting"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/kafka"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/league" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/league"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
@ -122,10 +121,10 @@ func main() {
// var userStore user.UserStore // var userStore user.UserStore
// Initialize producer // Initialize producer
topic := "wallet-balance-topic" // topic := "wallet-balance-topic"
producer := kafka.NewProducer(cfg.KafkaBrokers, topic) // producer := kafka.NewProducer(cfg.KafkaBrokers, topic)
notificationSvc := notificationservice.New(notificationRepo, domain.MongoDBLogger, logger, cfg, messengerSvc, userSvc, cfg.KafkaBrokers) notificationSvc := notificationservice.New(notificationRepo, domain.MongoDBLogger, logger, cfg, messengerSvc, userSvc)
walletSvc := wallet.NewService( walletSvc := wallet.NewService(
wallet.WalletStore(store), wallet.WalletStore(store),
@ -135,7 +134,6 @@ func main() {
userSvc, userSvc,
domain.MongoDBLogger, domain.MongoDBLogger,
logger, logger,
producer,
) )
branchSvc := branch.NewService(store) branchSvc := branch.NewService(store)
@ -195,8 +193,8 @@ func main() {
store, store,
) )
go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, veliVirtualGameService, "C:/Users/User/Desktop")
go httpserver.StartEnetPulseCron(enePulseSvc, domain.MongoDBLogger) go httpserver.StartEnetPulseCron(enePulseSvc, domain.MongoDBLogger)
go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, veliVirtualGameService, "C:/Users/User/Desktop")
go httpserver.ProcessBetCashback(context.TODO(), betSvc) go httpserver.ProcessBetCashback(context.TODO(), betSvc)
bankRepository := repository.NewBankRepository(store) bankRepository := repository.NewBankRepository(store)
@ -233,13 +231,13 @@ func main() {
fixerFertcherSvc, fixerFertcherSvc,
) )
exchangeWorker := currency.NewExchangeRateWorker(fixerFertcherSvc, logger, cfg) // exchangeWorker := currency.NewExchangeRateWorker(fixerFertcherSvc, logger, cfg)
exchangeWorker.Start(context.Background()) // exchangeWorker.Start(context.Background())
defer exchangeWorker.Stop() // defer exchangeWorker.Stop()
go walletMonitorSvc.Start() go walletMonitorSvc.Start()
httpserver.StartDataFetchingCrons(eventSvc, *oddsSvc, resultSvc, domain.MongoDBLogger) httpserver.StartDataFetchingCrons(eventSvc, *oddsSvc, resultSvc, domain.MongoDBLogger)
httpserver.StartTicketCrons(*ticketSvc, domain.MongoDBLogger) httpserver.StartCleanupCrons(*ticketSvc, notificationSvc, domain.MongoDBLogger)
issueReportingRepo := repository.NewReportedIssueRepository(store) issueReportingRepo := repository.NewReportedIssueRepository(store)

View File

@ -0,0 +1,221 @@
CREATE EXTENSION IF NOT EXISTS pgcrypto;
DO $$
DECLARE _admin_id bigint;
_manager_id bigint;
_company_wallet_id bigint;
_company_id bigint;
_branch_id bigint;
_branch_wallet_id bigint;
_cashier_id bigint;
BEGIN
INSERT INTO users (
first_name,
last_name,
email,
phone_number,
password,
role,
email_verified,
phone_verified,
created_at,
updated_at,
suspended
)
VALUES (
'Admin',
'BetFidel',
'admin.betfidel@gmail.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'admin',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE
) ON CONFLICT (email) DO
UPDATE
SET updated_at = EXCLUDED.updated_at
RETURNING id INTO STRICT _admin_id;
INSERT INTO users (
first_name,
last_name,
email,
phone_number,
password,
role,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
VALUES (
'Manager',
'BetFidel',
'manager.betfidel@gmail.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'branch_manager',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
_company_id
) ON CONFLICT (email) DO
UPDATE
SET updated_at = EXCLUDED.updated_at
RETURNING id INTO STRICT _manager_id;
INSERT INTO wallets (
balance,
is_withdraw,
is_bettable,
is_transferable,
user_id,
type,
currency,
is_active,
created_at,
updated_at
)
VALUES (
10000,
TRUE,
TRUE,
TRUE,
_admin_id,
'company_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
) ON CONFLICT (user_id, type) DO
UPDATE
SET updated_at = EXCLUDED.updated_at
RETURNING id INTO STRICT _company_wallet_id;
INSERT INTO companies (
name,
slug,
admin_id,
wallet_id,
deducted_percentage,
is_active,
created_at,
updated_at
)
VALUES (
'FidelBet',
'betfidel',
_admin_id,
_company_wallet_id,
0.15,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
) ON CONFLICT (slug) DO
UPDATE
SET updated_at = EXCLUDED.updated_at
RETURNING id INTO STRICT _company_id;
UPDATE users
SET company_id = _company_id
WHERE id = _admin_id;
INSERT INTO wallets (
balance,
is_withdraw,
is_bettable,
is_transferable,
user_id,
type,
currency,
is_active,
created_at,
updated_at
)
VALUES (
10000,
TRUE,
TRUE,
TRUE,
_admin_id,
'branch_wallet',
'ETB',
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
) ON CONFLICT (user_id, type) DO
UPDATE
SET updated_at = EXCLUDED.updated_at
RETURNING id INTO STRICT _branch_wallet_id;
INSERT INTO branches (
name,
location,
wallet_id,
branch_manager_id,
company_id,
is_self_owned,
profit_percent,
is_active,
created_at,
updated_at
)
VALUES (
'Test Branch',
'addis_ababa',
_branch_wallet_id,
_manager_id,
_company_id,
TRUE,
0.10,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
) ON CONFLICT (wallet_id) DO
UPDATE
SET updated_at = EXCLUDED.updated_at
RETURNING id INTO STRICT _branch_id;
INSERT INTO users (
first_name,
last_name,
email,
phone_number,
password,
role,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
VALUES (
'Cashier',
'BetFidel',
'cashier.betfidel@gmail.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'cashier',
TRUE,
FALSE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
FALSE,
_company_id
) ON CONFLICT (email) DO
UPDATE
SET updated_at = EXCLUDED.updated_at
RETURNING id INTO STRICT _cashier_id;
INSERT INTO branch_cashiers (user_id, branch_id)
VALUES (_cashier_id, _branch_id);
RAISE NOTICE 'BETFIDEL_DEV_DATA (Admin ID: %, Company Wallet ID: %, Company ID: %)',
_admin_id,
_company_wallet_id,
_company_id;
RAISE NOTICE 'BETFIDEL_DEV_DATA (Branch ID: %, Branch Wallet ID: %, Manager ID: %)',
_branch_id,
_branch_wallet_id,
_manager_id;
RAISE NOTICE 'BETFIDEL_DEV_DATA (Cashier ID: %)',
_cashier_id;
END $$;

View File

@ -73,10 +73,9 @@ CREATE TABLE IF NOT EXISTS wallets (
is_active BOOLEAN NOT NULL DEFAULT true, is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, type) UNIQUE(user_id, type),
CONSTRAINT balance_positve CHECK (balance >= 0)
); );
CREATE TABLE refresh_tokens ( CREATE TABLE refresh_tokens (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL, user_id BIGINT NOT NULL,
@ -186,19 +185,19 @@ CREATE TABLE IF NOT EXISTS banks (
currency VARCHAR(10) NOT NULL, currency VARCHAR(10) NOT NULL,
bank_logo TEXT -- URL or base64 string bank_logo TEXT -- URL or base64 string
); );
CREATE TABLE IF NOT EXISTS wallets ( -- CREATE TABLE IF NOT EXISTS wallets (
id BIGSERIAL PRIMARY KEY, -- id BIGSERIAL PRIMARY KEY,
balance BIGINT NOT NULL DEFAULT 0, -- balance BIGINT NOT NULL DEFAULT 0,
is_withdraw BOOLEAN NOT NULL, -- is_withdraw BOOLEAN NOT NULL,
is_bettable BOOLEAN NOT NULL, -- is_bettable BOOLEAN NOT NULL,
is_transferable BOOLEAN NOT NULL, -- is_transferable BOOLEAN NOT NULL,
user_id BIGINT NOT NULL, -- user_id BIGINT NOT NULL,
type VARCHAR(255) NOT NULL, -- type VARCHAR(255) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT true, -- is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT balance_positve CHECK (balance >= 0) -- CONSTRAINT balance_positve CHECK (balance >= 0)
); -- );
CREATE TABLE IF NOT EXISTS customer_wallets ( CREATE TABLE IF NOT EXISTS customer_wallets (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL, customer_id BIGINT NOT NULL,
@ -272,7 +271,7 @@ CREATE TABLE IF NOT EXISTS branches (
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
location TEXT NOT NULL, location TEXT NOT NULL,
profit_percent REAL NOT NULL, profit_percent REAL NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT false, is_active BOOLEAN NOT NULL DEFAULT true,
wallet_id BIGINT NOT NULL, wallet_id BIGINT NOT NULL,
branch_manager_id BIGINT NOT NULL, branch_manager_id BIGINT NOT NULL,
company_id BIGINT NOT NULL, company_id BIGINT NOT NULL,
@ -321,6 +320,7 @@ CREATE TABLE events (
is_live BOOLEAN NOT NULL DEFAULT false, is_live BOOLEAN NOT NULL DEFAULT false,
status TEXT NOT NULL, status TEXT NOT NULL,
fetched_at TIMESTAMP DEFAULT now (), fetched_at TIMESTAMP DEFAULT now (),
updated_at TIMESTAMP DEFAULT now (),
source TEXT NOT NULL DEFAULT 'b365api' CHECK ( source TEXT NOT NULL DEFAULT 'b365api' CHECK (
source IN ('b365api', 'bfair', '1xbet', 'bwin', 'enetpulse') source IN ('b365api', 'bfair', '1xbet', 'bwin', 'enetpulse')
), ),
@ -342,7 +342,7 @@ CREATE TABLE company_event_settings (
event_id BIGINT NOT NULL, event_id BIGINT NOT NULL,
is_active BOOLEAN, is_active BOOLEAN,
is_featured BOOLEAN, is_featured BOOLEAN,
winning_upper_limit INT, winning_upper_limit BIGINT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (company_id, event_id) UNIQUE (company_id, event_id)
); );
@ -354,6 +354,7 @@ CREATE TABLE odds_market (
market_category TEXT NOT NULL, market_category TEXT NOT NULL,
market_id BIGINT NOT NULL, market_id BIGINT NOT NULL,
raw_odds JSONB NOT NULL, raw_odds JSONB NOT NULL,
number_of_outcomes BIGINT NOT NULL,
default_is_active BOOLEAN NOT NULL DEFAULT true, default_is_active BOOLEAN NOT NULL DEFAULT true,
fetched_at TIMESTAMP DEFAULT now (), fetched_at TIMESTAMP DEFAULT now (),
expires_at TIMESTAMP NOT NULL, expires_at TIMESTAMP NOT NULL,
@ -408,11 +409,11 @@ CREATE TABLE companies (
admin_id BIGINT NOT NULL, admin_id BIGINT NOT NULL,
wallet_id BIGINT NOT NULL, wallet_id BIGINT NOT NULL,
deducted_percentage REAL NOT NULL, deducted_percentage REAL NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT false, is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT deducted_percentage_check CHECK ( CONSTRAINT deducted_percentage_check CHECK (
deducted_percentage >= 0 deducted_percentage > 0
AND deducted_percentage < 1 AND deducted_percentage < 1
) )
); );
@ -511,6 +512,8 @@ CREATE TABLE IF NOT EXISTS raffles (
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(), created_at TIMESTAMP NOT NULL DEFAULT NOW(),
expires_at TIMESTAMP NOT NULL, expires_at TIMESTAMP NOT NULL,
-- -1 means there is no limit for the raffle
ticket_limit INT NOT NULL DEFAULT -1,
type VARCHAR(50) NOT NULL CHECK (type IN ('virtual', 'sport')), type VARCHAR(50) NOT NULL CHECK (type IN ('virtual', 'sport')),
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'completed')) status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'completed'))
); );
@ -580,14 +583,17 @@ CREATE VIEW bet_with_outcomes AS
SELECT bets.*, 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, users.phone_number,
JSON_AGG (bet_outcomes.*) AS outcomes JSON_AGG (bet_outcomes.*) AS outcomes,
companies.slug as company_slug
FROM bets FROM bets
LEFT JOIN bet_outcomes ON bets.id = bet_outcomes.bet_id LEFT JOIN bet_outcomes ON bets.id = bet_outcomes.bet_id
LEFT JOIN users ON bets.user_id = users.id LEFT JOIN users ON bets.user_id = users.id
JOIN companies ON bets.company_id = companies.id
GROUP BY bets.id, GROUP BY bets.id,
users.first_name, users.first_name,
users.last_name, users.last_name,
users.phone_number; users.phone_number,
companies.slug;
CREATE VIEW ticket_with_outcomes AS CREATE VIEW ticket_with_outcomes AS
SELECT tickets.*, SELECT tickets.*,
JSON_AGG (ticket_outcomes.*) AS outcomes JSON_AGG (ticket_outcomes.*) AS outcomes
@ -644,6 +650,7 @@ SELECT sb.*,
st.verified AS transaction_verified, st.verified AS transaction_verified,
bets.status, bets.status,
bets.total_odds, bets.total_odds,
bets.fast_code,
JSON_AGG (bet_outcomes.*) AS outcomes JSON_AGG (bet_outcomes.*) AS outcomes
FROM shop_bets AS sb FROM shop_bets AS sb
JOIN shop_transactions st ON st.id = sb.shop_transaction_id JOIN shop_transactions st ON st.id = sb.shop_transaction_id
@ -657,7 +664,8 @@ GROUP BY sb.id,
st.amount, st.amount,
st.verified, st.verified,
bets.status, bets.status,
bets.total_odds; bets.total_odds,
bets.fast_code;
CREATE VIEW shop_deposit_detail AS CREATE VIEW shop_deposit_detail AS
SELECT sd.*, SELECT sd.*,
st.full_name, st.full_name,
@ -685,16 +693,30 @@ SELECT e.*,
ces.winning_upper_limit, ces.winning_upper_limit,
e.default_winning_upper_limit e.default_winning_upper_limit
) AS winning_upper_limit, ) AS winning_upper_limit,
ces.updated_at, ces.updated_at as company_updated_at,
l.country_code as league_cc l.country_code as league_cc,
COALESCE(om.total_outcomes, 0) AS total_outcomes
FROM events e FROM events e
LEFT 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; JOIN leagues l ON l.id = e.league_id
LEFT JOIN (
SELECT event_id,
SUM(number_of_outcomes) AS total_outcomes
FROM odds_market
GROUP BY event_id
) om ON om.event_id = e.id;
CREATE VIEW event_with_country AS CREATE VIEW event_with_country AS
SELECT events.*, SELECT events.*,
leagues.country_code as league_cc leagues.country_code as league_cc,
COALESCE(om.total_outcomes, 0) AS total_outcomes
FROM events FROM events
LEFT JOIN leagues ON leagues.id = events.league_id; LEFT JOIN leagues ON leagues.id = events.league_id
LEFT JOIN (
SELECT event_id,
SUM(number_of_outcomes) AS total_outcomes
FROM odds_market
GROUP BY event_id
) om ON om.event_id = events.id;
CREATE VIEW odds_market_with_settings AS CREATE VIEW odds_market_with_settings AS
SELECT o.id, SELECT o.id,
o.event_id, o.event_id,
@ -702,6 +724,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,

View File

@ -37,6 +37,8 @@ CREATE TABLE IF NOT EXISTS notifications (
priority INTEGER, priority INTEGER,
version INTEGER NOT NULL DEFAULT 0, version INTEGER NOT NULL DEFAULT 0,
timestamp TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, timestamp TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
img TEXT,
expires TIMESTAMPTZ NOT NULL,
metadata JSONB metadata JSONB
); );
CREATE TABLE IF NOT EXISTS wallet_threshold_notifications ( CREATE TABLE IF NOT EXISTS wallet_threshold_notifications (

View File

@ -2,4 +2,10 @@ DROP TABLE IF EXISTS enetpulse_sports;
DROP TABLE IF EXISTS enetpulse_tournament_templates; DROP TABLE IF EXISTS enetpulse_tournament_templates;
DROP TABLE IF EXISTS enetpulse_tournaments; DROP TABLE IF EXISTS enetpulse_tournaments;
DROP TABLE IF EXISTS enetpulse_tournament_stages; DROP TABLE IF EXISTS enetpulse_tournament_stages;
DROP TABLE IF EXISTS enetpulse_fixtures; DROP TABLE IF EXISTS enetpulse_fixtures;
DROP TABLE IF EXISTS enetpulse_results;
DROP TABLE IF EXISTS enetpulse_result_participants;
DROP TABLE IF EXISTS enetpulse_result_referees;
DROP TABLE IF EXISTS enetpulse_outcome_types;
DROP TABLE IF EXISTS enetpulse_preodds;
DROP TABLE IF EXISTS enetpulse_preodds_bettingoffers;

View File

@ -133,6 +133,40 @@ SELECT *
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE status = 2 WHERE status = 2
AND processed = false; AND processed = false;
-- name: GetBetOutcomeViewByEventID :many
SELECT bet_outcomes.*,
users.first_name,
users.last_name,
bets.amount,
bets.total_odds,
companies.name as company_name
FROM bet_outcomes
JOIN bets ON bets.id = bet_outcomes.bet_id
JOIN users ON bets.user_id = users.id
JOIN companies ON bets.company_id = companies.id
WHERE bet_outcomes.event_id = $1
AND (
bets.company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
bet_outcomes.status = sqlc.narg('filter_status')
OR sqlc.narg('filter_status') IS NULL
)
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: TotalBetOutcomeViewByEventID :one
SELECT count(*)
FROM bet_outcomes
JOIN bets ON bets.id = bet_outcomes.bet_id
WHERE bet_outcomes.event_id = $1
AND (
bets.company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
bet_outcomes.status = sqlc.narg('filter_status')
OR sqlc.narg('filter_status') IS NULL
);
-- name: GetBetOutcomeByEventID :many -- name: GetBetOutcomeByEventID :many
SELECT * SELECT *
FROM bet_outcomes FROM bet_outcomes
@ -180,6 +214,15 @@ UPDATE bet_outcomes
SEt status = $1 SEt status = $1
WHERE event_id = $2 WHERE event_id = $2
RETURNING *; RETURNING *;
-- name: UpdateBetOutcomeStatusForOddID :many
UPDATE bet_outcomes
SEt status = $1
WHERE odd_id = $2
RETURNING *;
-- name: BulkUpdateBetOutcomeStatusByOddIDs :exec
UPDATE bet_outcomes
SET status = $1
WHERE odd_id = ANY(sqlc.arg('odd_ids')::BIGINT []);
-- name: UpdateStatus :exec -- name: UpdateStatus :exec
UPDATE bets UPDATE bets
SET status = $1, SET status = $1,

View File

@ -30,6 +30,10 @@ wHERE (
user_id = sqlc.narg('user_id') user_id = sqlc.narg('user_id')
OR sqlc.narg('user_id') IS NULL OR sqlc.narg('user_id') IS NULL
) )
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND ( AND (
created_at > sqlc.narg('created_before') created_at > sqlc.narg('created_before')
OR sqlc.narg('created_before') IS NULL OR sqlc.narg('created_before') IS NULL
@ -60,6 +64,10 @@ wHERE (
user_id = sqlc.narg('user_id') user_id = sqlc.narg('user_id')
OR sqlc.narg('user_id') IS NULL OR sqlc.narg('user_id') IS NULL
) )
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND ( AND (
is_shop_bet = sqlc.narg('is_shop_bet') is_shop_bet = sqlc.narg('is_shop_bet')
OR sqlc.narg('is_shop_bet') IS NULL OR sqlc.narg('is_shop_bet') IS NULL
@ -117,6 +125,10 @@ WITH market_counts AS (
user_id = sqlc.narg('user_id') user_id = sqlc.narg('user_id')
OR sqlc.narg('user_id') IS NULL OR sqlc.narg('user_id') IS NULL
) )
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND ( AND (
created_at > sqlc.narg('created_before') created_at > sqlc.narg('created_before')
OR sqlc.narg('created_before') IS NULL OR sqlc.narg('created_before') IS NULL

View File

@ -65,7 +65,11 @@ WHERE branch_manager_id = $1;
-- name: SearchBranchByName :many -- name: SearchBranchByName :many
SELECT * SELECT *
FROM branch_details FROM branch_details
WHERE name ILIKE '%' || $1 || '%'; WHERE name ILIKE '%' || $1 || '%'
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
);
-- name: GetAllSupportedOperations :many -- name: GetAllSupportedOperations :many
SELECT * SELECT *
FROM supported_operations; FROM supported_operations;

View File

@ -4,9 +4,10 @@ INSERT INTO companies (
slug, slug,
admin_id, admin_id,
wallet_id, wallet_id,
deducted_percentage deducted_percentage,
is_active
) )
VALUES ($1, $2, $3, $4, $5) VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *; RETURNING *;
-- name: GetAllCompanies :many -- name: GetAllCompanies :many
SELECT * SELECT *
@ -30,15 +31,15 @@ WHERE (
SELECT * SELECT *
FROM companies_details FROM companies_details
WHERE id = $1; WHERE id = $1;
-- name: GetCompanyIDUsingSlug :one -- name: GetCompanyUsingSlug :one
SELECT id SELECT *
FROM companies FROM companies
WHERE slug = $1; WHERE slug = $1;
-- name: SearchCompanyByName :many -- name: SearchCompanyByName :many
SELECT * SELECT *
FROM companies_details FROM companies_details
WHERE name ILIKE '%' || $1 || '%'; WHERE name ILIKE '%' || $1 || '%';
-- name: UpdateCompany :one -- name: UpdateCompany :exec
UPDATE companies UPDATE companies
SET name = COALESCE(sqlc.narg(name), name), SET name = COALESCE(sqlc.narg(name), name),
admin_id = COALESCE(sqlc.narg(admin_id), admin_id), admin_id = COALESCE(sqlc.narg(admin_id), admin_id),
@ -47,9 +48,9 @@ SET name = COALESCE(sqlc.narg(name), name),
sqlc.narg(deducted_percentage), sqlc.narg(deducted_percentage),
deducted_percentage deducted_percentage
), ),
slug = COALESCE(sqlc.narg(slug), slug),
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
WHERE id = $1 WHERE id = $1;
RETURNING *;
-- name: DeleteCompany :exec -- name: DeleteCompany :exec
DELETE FROM companies DELETE FROM companies
WHERE id = $1; WHERE id = $1;

View File

@ -107,7 +107,8 @@ INSERT INTO enetpulse_tournament_stages (
updates_count, updates_count,
last_updated_at, last_updated_at,
status status
) VALUES ( )
VALUES (
$1, -- stage_id $1, -- stage_id
$2, -- name $2, -- name
$3, -- tournament_fk $3, -- tournament_fk
@ -120,6 +121,19 @@ INSERT INTO enetpulse_tournament_stages (
$10, -- last_updated_at $10, -- last_updated_at
$11 -- status $11 -- status
) )
ON CONFLICT (stage_id) DO UPDATE
SET
name = EXCLUDED.name,
tournament_fk = EXCLUDED.tournament_fk,
gender = EXCLUDED.gender,
country_fk = EXCLUDED.country_fk,
country_name = EXCLUDED.country_name,
start_date = EXCLUDED.start_date,
end_date = EXCLUDED.end_date,
updates_count = EXCLUDED.updates_count,
last_updated_at = EXCLUDED.last_updated_at,
status = EXCLUDED.status,
updated_at = NOW()
RETURNING *; RETURNING *;
-- name: GetAllEnetpulseTournamentStages :many -- name: GetAllEnetpulseTournamentStages :many
@ -367,6 +381,48 @@ SELECT *
FROM enetpulse_preodds_bettingoffers FROM enetpulse_preodds_bettingoffers
ORDER BY created_at DESC; ORDER BY created_at DESC;
-- name: GetFixturesWithPreodds :many
SELECT
f.fixture_id AS id,
f.fixture_id AS fixture_id,
f.name AS fixture_name,
f.sport_fk,
f.tournament_fk,
f.tournament_template_fk,
f.tournament_stage_fk,
f.start_date,
f.status_type,
f.status_desc_fk,
f.round_type_fk,
f.updates_count AS fixture_updates_count,
f.last_updated_at AS fixture_last_updated_at,
f.created_at AS fixture_created_at,
f.updated_at AS fixture_updated_at,
-- Preodds fields
p.id AS preodds_db_id,
p.preodds_id,
p.event_fk,
p.outcome_type_fk,
p.outcome_scope_fk,
p.outcome_subtype_fk,
p.event_participant_number,
p.iparam,
p.iparam2,
p.dparam,
p.dparam2,
p.sparam,
p.updates_count AS preodds_updates_count,
p.last_updated_at AS preodds_last_updated_at,
p.created_at AS preodds_created_at,
p.updated_at AS preodds_updated_at
FROM enetpulse_fixtures f
LEFT JOIN enetpulse_preodds p
ON p.event_fk = f.id
ORDER BY f.start_date DESC;

View File

@ -56,7 +56,7 @@ SET sport_id = EXCLUDED.sport_id,
source = EXCLUDED.source, source = EXCLUDED.source,
default_winning_upper_limit = EXCLUDED.default_winning_upper_limit, default_winning_upper_limit = EXCLUDED.default_winning_upper_limit,
fetched_at = now(); fetched_at = now();
-- name: SaveEventSettings :exec -- name: SaveTenantEventSettings :exec
INSERT INTO company_event_settings ( INSERT INTO company_event_settings (
company_id, company_id,
event_id, event_id,
@ -218,11 +218,18 @@ SELECT e.*,
e.default_winning_upper_limit e.default_winning_upper_limit
) AS winning_upper_limit, ) AS winning_upper_limit,
ces.updated_at, ces.updated_at,
l.country_code as league_cc l.country_code as league_cc,
COALESCE(om.total_outcomes, 0) AS total_outcomes
FROM events e FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $1 AND ces.company_id = $1
JOIN leagues l ON l.id = e.league_id JOIN leagues l ON l.id = e.league_id
LEFT JOIN (
SELECT event_id,
SUM(number_of_outcomes) AS total_outcomes
FROM odds_market
GROUP BY event_id
) om ON om.event_id = e.id
WHERE ( WHERE (
is_live = sqlc.narg('is_live') is_live = sqlc.narg('is_live')
OR sqlc.narg('is_live') IS NULL OR sqlc.narg('is_live') IS NULL
@ -292,15 +299,24 @@ SELECT e.*,
e.default_winning_upper_limit e.default_winning_upper_limit
) AS winning_upper_limit, ) AS winning_upper_limit,
ces.updated_at, ces.updated_at,
l.country_code as league_cc l.country_code as league_cc,
COALESCE(om.total_outcomes, 0) AS total_outcomes
FROM events e FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $2 AND ces.company_id = $2
JOIN leagues l ON l.id = e.league_id JOIN leagues l ON l.id = e.league_id
LEFT JOIN (
SELECT event_id,
SUM(number_of_outcomes) AS total_outcomes
FROM odds_market
GROUP BY event_id
) om ON om.event_id = e.id
WHERE e.id = $1 WHERE e.id = $1
LIMIT 1; LIMIT 1;
-- name: GetSportAndLeagueIDs :one -- name: GetSportAndLeagueIDs :one
SELECT sport_id, league_id FROM events SELECT sport_id,
league_id
FROM events
WHERE id = $1; WHERE id = $1;
-- name: UpdateMatchResult :exec -- name: UpdateMatchResult :exec
UPDATE events UPDATE events
@ -313,8 +329,22 @@ FROM events
WHERE id = $1; WHERE id = $1;
-- name: UpdateEventMonitored :exec -- name: UpdateEventMonitored :exec
UPDATE events UPDATE events
SET is_monitored = $1 SET is_monitored = $1,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2; WHERE id = $2;
-- name: UpdateGlobalEventSettings :exec
UPDATE events
SET default_is_active = COALESCE(sqlc.narg(default_is_active), default_is_active),
default_is_featured = COALESCE(
sqlc.narg(default_is_featured),
default_is_featured
),
default_winning_upper_limit = COALESCE(
sqlc.narg(default_winning_upper_limit),
default_winning_upper_limit
),
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;
-- name: DeleteEvent :exec -- name: DeleteEvent :exec
DELETE FROM events DELETE FROM events
WHERE id = $1; WHERE id = $1;

View File

@ -14,7 +14,7 @@ SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code, country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id, bet365_id = EXCLUDED.bet365_id,
sport_id = EXCLUDED.sport_id; sport_id = EXCLUDED.sport_id;
-- name: InsertLeagueSettings :exec -- name: SaveLeagueSettings :exec
INSERT INTO company_league_settings ( INSERT INTO company_league_settings (
company_id, company_id,
league_id, league_id,
@ -40,8 +40,31 @@ WHERE (
name ILIKE '%' || sqlc.narg('query') || '%' name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL OR sqlc.narg('query') IS NULL
) )
AND (
default_is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') IS NULL
)
ORDER BY name ASC ORDER BY name ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetTotalLeagues :one
SELECT COUNT(*)
FROM leagues
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 (
name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
default_is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') IS NULL
);
-- name: GetTotalLeaguesWithSettings :one -- name: GetTotalLeaguesWithSettings :one
SELECT COUNT(*) SELECT COUNT(*)
FROM leagues l FROM leagues l
@ -118,7 +141,7 @@ SET name = COALESCE(sqlc.narg('name'), name),
bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id), bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id),
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id) sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
WHERE id = $1; WHERE id = $1;
-- name: UpdateLeagueSettings :exec -- name: UpdateCompanyLeagueSettings :exec
UPDATE company_league_settings UPDATE company_league_settings
SET is_active = COALESCE(sqlc.narg('is_active'), is_active), SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured = COALESCE( is_featured = COALESCE(
@ -126,4 +149,9 @@ SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured is_featured
) )
WHERE league_id = $1 WHERE league_id = $1
AND company_id = $2; AND company_id = $2;
-- name: UpdateGlobalLeagueSettings :exec
UPDATE leagues
SET default_is_active = COALESCE(sqlc.narg('is_active'), default_is_active),
default_is_featured = COALESCE(sqlc.narg('is_featured'), default_is_featured)
WHERE id = $1;

View File

@ -12,6 +12,8 @@ INSERT INTO notifications (
payload, payload,
priority, priority,
timestamp, timestamp,
expires,
img,
metadata metadata
) )
VALUES ( VALUES (
@ -27,7 +29,9 @@ VALUES (
$10, $10,
$11, $11,
$12, $12,
$13 $13,
$14,
$15
) )
RETURNING *; RETURNING *;
-- name: GetNotification :one -- name: GetNotification :one
@ -88,4 +92,8 @@ SELECT COUNT(*) as total,
WHEN is_read = false THEN 1 WHEN is_read = false THEN 1
END END
) as unread ) as unread
FROM notifications; FROM notifications;
-- name: DeleteOldNotifications :exec
DELETE FROM notifications
WHERE expires < now();

View File

@ -5,6 +5,7 @@ INSERT INTO odds_market (
market_name, market_name,
market_category, market_category,
market_id, market_id,
number_of_outcomes,
raw_odds, raw_odds,
fetched_at, fetched_at,
expires_at expires_at
@ -17,13 +18,15 @@ VALUES (
$5, $5,
$6, $6,
$7, $7,
$8 $8,
$9
) ON CONFLICT (event_id, market_id) DO ) ON CONFLICT (event_id, market_id) DO
UPDATE UPDATE
SET market_type = EXCLUDED.market_type, SET market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name, market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category, market_category = EXCLUDED.market_category,
raw_odds = EXCLUDED.raw_odds, raw_odds = EXCLUDED.raw_odds,
number_of_outcomes = EXCLUDED.number_of_outcomes,
fetched_at = EXCLUDED.fetched_at, fetched_at = EXCLUDED.fetched_at,
expires_at = EXCLUDED.expires_at; expires_at = EXCLUDED.expires_at;
-- name: SaveOddSettings :exec -- name: SaveOddSettings :exec
@ -48,6 +51,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,
@ -75,6 +79,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,
@ -94,6 +99,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,
@ -129,6 +135,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,
@ -143,4 +150,15 @@ WHERE event_id = $1
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: DeleteOddsForEvent :exec -- name: DeleteOddsForEvent :exec
DELETE FROM odds_market DELETE FROM odds_market
Where event_id = $1; Where event_id = $1;
-- name: DeleteAllCompanyOddsSetting :exec
DELETE FROM company_odd_settings
WHERE company_id = $1;
-- name: DeleteCompanyOddsSettingByOddMarketID :exec
DELETE FROM company_odd_settings
WHERE company_id = $1
AND odds_market_id = $2;
-- name: UpdateGlobalOddsSetting :exec
UPDATE odds_market
SET default_is_active = COALESCE(sqlc.narg(default_is_active), default_is_active)
WHERE id = $1;

View File

@ -1,6 +1,6 @@
-- name: CreateRaffle :one -- name: CreateRaffle :one
INSERT INTO raffles (company_id, name, expires_at, type) INSERT INTO raffles (company_id, name, expires_at, ticket_limit, type)
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4, $5)
RETURNING *; RETURNING *;
-- name: GetRafflesOfCompany :many -- name: GetRafflesOfCompany :many
@ -71,3 +71,19 @@ FROM raffle_sport_filters
WHERE raffle_id = $1 WHERE raffle_id = $1
AND sport_id = $2 AND sport_id = $2
AND league_id = $3; AND league_id = $3;
-- name: CheckSportRaffleHasFilter :one
SELECT EXISTS (
SELECT 1 FROM raffle_sport_filters WHERE raffle_id = $1
) AS has_filter;
-- name: GetRaffleTicketLimit :one
SELECT ticket_limit
FROM raffles
WHERE id = $1;
-- name: GetRaffleTicketCount :one
SELECT COUNT(*)
FROM raffle_tickets
WHERE raffle_id = $1
AND user_id = $2;

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

@ -25,7 +25,7 @@ services:
image: mongo:7.0.11 image: mongo:7.0.11
restart: always restart: always
ports: ports:
- "27017:27017" - "27022:27017"
environment: environment:
MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: secret MONGO_INITDB_ROOT_PASSWORD: secret
@ -56,47 +56,17 @@ services:
] ]
networks: networks:
- app - app
# redis:
redis: # image: redis:7-alpine
image: redis:7-alpine # ports:
ports: # - "6379:6379"
- "6379:6379" # networks:
networks: # - app
- app # healthcheck:
healthcheck: # test: ["CMD", "redis-cli", "ping"]
test: ["CMD", "redis-cli", "ping"] # interval: 10s
interval: 10s # timeout: 5s
timeout: 5s # retries: 5
retries: 5
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
container_name: zookeeper
ports:
- "22181:2181" # remapped host port (Windows-safe)
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
networks:
- app
kafka:
image: confluentinc/cp-kafka:7.5.0
depends_on:
- zookeeper
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,PLAINTEXT_HOST://0.0.0.0:29092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
ports:
- "9092:9092"
- "29092:29092"
networks:
- app
app: app:
build: build:
context: . context: .
@ -107,15 +77,9 @@ services:
environment: environment:
- DB_URL=postgresql://root:secret@postgres:5432/gh?sslmode=disable - DB_URL=postgresql://root:secret@postgres:5432/gh?sslmode=disable
- MONGO_URI=mongodb://root:secret@mongo:27017 - MONGO_URI=mongodb://root:secret@mongo:27017
- REDIS_ADDR=redis:6379
- KAFKA_BROKERS=kafka:9092
depends_on: depends_on:
migrate: migrate:
condition: service_completed_successfully condition: service_completed_successfully
mongo:
condition: service_healthy
redis:
condition: service_healthy
networks: networks:
- app - app
command: ["/app/bin/web"] command: ["/app/bin/web"]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,22 @@ import (
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
const BulkUpdateBetOutcomeStatusByOddIDs = `-- name: BulkUpdateBetOutcomeStatusByOddIDs :exec
UPDATE bet_outcomes
SET status = $1
WHERE odd_id = ANY($2::BIGINT [])
`
type BulkUpdateBetOutcomeStatusByOddIDsParams struct {
Status int32 `json:"status"`
OddIds []int64 `json:"odd_ids"`
}
func (q *Queries) BulkUpdateBetOutcomeStatusByOddIDs(ctx context.Context, arg BulkUpdateBetOutcomeStatusByOddIDsParams) error {
_, err := q.db.Exec(ctx, BulkUpdateBetOutcomeStatusByOddIDs, arg.Status, arg.OddIds)
return err
}
const CreateBet = `-- name: CreateBet :one const CreateBet = `-- name: CreateBet :one
INSERT INTO bets ( INSERT INTO bets (
amount, amount,
@ -104,7 +120,7 @@ func (q *Queries) DeleteBetOutcome(ctx context.Context, betID int64) error {
} }
const GetAllBets = `-- name: GetAllBets :many const GetAllBets = `-- name: GetAllBets :many
SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug
FROM bet_with_outcomes FROM bet_with_outcomes
wHERE ( wHERE (
user_id = $1 user_id = $1
@ -192,6 +208,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.Outcomes, &i.Outcomes,
&i.CompanySlug,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -204,7 +221,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
} }
const GetBetByFastCode = `-- name: GetBetByFastCode :one const GetBetByFastCode = `-- name: GetBetByFastCode :one
SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE fast_code = $1 WHERE fast_code = $1
LIMIT 1 LIMIT 1
@ -230,12 +247,13 @@ func (q *Queries) GetBetByFastCode(ctx context.Context, fastCode string) (BetWit
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.Outcomes, &i.Outcomes,
&i.CompanySlug,
) )
return i, err return i, err
} }
const GetBetByID = `-- name: GetBetByID :one const GetBetByID = `-- name: GetBetByID :one
SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE id = $1 WHERE id = $1
` `
@ -260,12 +278,13 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.Outcomes, &i.Outcomes,
&i.CompanySlug,
) )
return i, err return i, err
} }
const GetBetByUserID = `-- name: GetBetByUserID :many const GetBetByUserID = `-- name: GetBetByUserID :many
SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE user_id = $1 WHERE user_id = $1
` `
@ -296,6 +315,7 @@ func (q *Queries) GetBetByUserID(ctx context.Context, userID int64) ([]BetWithOu
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.Outcomes, &i.Outcomes,
&i.CompanySlug,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -448,8 +468,109 @@ func (q *Queries) GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (i
return count, err return count, err
} }
const GetBetOutcomeViewByEventID = `-- name: GetBetOutcomeViewByEventID :many
SELECT bet_outcomes.id, bet_outcomes.bet_id, bet_outcomes.sport_id, bet_outcomes.event_id, bet_outcomes.odd_id, bet_outcomes.home_team_name, bet_outcomes.away_team_name, bet_outcomes.market_id, bet_outcomes.market_name, bet_outcomes.odd, bet_outcomes.odd_name, bet_outcomes.odd_header, bet_outcomes.odd_handicap, bet_outcomes.status, bet_outcomes.expires,
users.first_name,
users.last_name,
bets.amount,
bets.total_odds,
companies.name as company_name
FROM bet_outcomes
JOIN bets ON bets.id = bet_outcomes.bet_id
JOIN users ON bets.user_id = users.id
JOIN companies ON bets.company_id = companies.id
WHERE bet_outcomes.event_id = $1
AND (
bets.company_id = $2
OR $2 IS NULL
)
AND (
bet_outcomes.status = $3
OR $3 IS NULL
)
LIMIT $5 OFFSET $4
`
type GetBetOutcomeViewByEventIDParams struct {
EventID int64 `json:"event_id"`
CompanyID pgtype.Int8 `json:"company_id"`
FilterStatus pgtype.Int4 `json:"filter_status"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
type GetBetOutcomeViewByEventIDRow struct {
ID int64 `json:"id"`
BetID int64 `json:"bet_id"`
SportID int64 `json:"sport_id"`
EventID int64 `json:"event_id"`
OddID int64 `json:"odd_id"`
HomeTeamName string `json:"home_team_name"`
AwayTeamName string `json:"away_team_name"`
MarketID int64 `json:"market_id"`
MarketName string `json:"market_name"`
Odd float32 `json:"odd"`
OddName string `json:"odd_name"`
OddHeader string `json:"odd_header"`
OddHandicap string `json:"odd_handicap"`
Status int32 `json:"status"`
Expires pgtype.Timestamp `json:"expires"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
CompanyName string `json:"company_name"`
}
func (q *Queries) GetBetOutcomeViewByEventID(ctx context.Context, arg GetBetOutcomeViewByEventIDParams) ([]GetBetOutcomeViewByEventIDRow, error) {
rows, err := q.db.Query(ctx, GetBetOutcomeViewByEventID,
arg.EventID,
arg.CompanyID,
arg.FilterStatus,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetBetOutcomeViewByEventIDRow
for rows.Next() {
var i GetBetOutcomeViewByEventIDRow
if err := rows.Scan(
&i.ID,
&i.BetID,
&i.SportID,
&i.EventID,
&i.OddID,
&i.HomeTeamName,
&i.AwayTeamName,
&i.MarketID,
&i.MarketName,
&i.Odd,
&i.OddName,
&i.OddHeader,
&i.OddHandicap,
&i.Status,
&i.Expires,
&i.FirstName,
&i.LastName,
&i.Amount,
&i.TotalOdds,
&i.CompanyName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetBetsForCashback = `-- name: GetBetsForCashback :many const GetBetsForCashback = `-- name: GetBetsForCashback :many
SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes SELECT id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE status = 2 WHERE status = 2
AND processed = false AND processed = false
@ -481,6 +602,7 @@ func (q *Queries) GetBetsForCashback(ctx context.Context) ([]BetWithOutcome, err
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.Outcomes, &i.Outcomes,
&i.CompanySlug,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -557,6 +679,34 @@ func (q *Queries) GetTotalBets(ctx context.Context, arg GetTotalBetsParams) (int
return count, err return count, err
} }
const TotalBetOutcomeViewByEventID = `-- name: TotalBetOutcomeViewByEventID :one
SELECT count(*)
FROM bet_outcomes
JOIN bets ON bets.id = bet_outcomes.bet_id
WHERE bet_outcomes.event_id = $1
AND (
bets.company_id = $2
OR $2 IS NULL
)
AND (
bet_outcomes.status = $3
OR $3 IS NULL
)
`
type TotalBetOutcomeViewByEventIDParams struct {
EventID int64 `json:"event_id"`
CompanyID pgtype.Int8 `json:"company_id"`
FilterStatus pgtype.Int4 `json:"filter_status"`
}
func (q *Queries) TotalBetOutcomeViewByEventID(ctx context.Context, arg TotalBetOutcomeViewByEventIDParams) (int64, error) {
row := q.db.QueryRow(ctx, TotalBetOutcomeViewByEventID, arg.EventID, arg.CompanyID, arg.FilterStatus)
var count int64
err := row.Scan(&count)
return count, err
}
const UpdateBetOutcomeStatus = `-- name: UpdateBetOutcomeStatus :one const UpdateBetOutcomeStatus = `-- name: UpdateBetOutcomeStatus :one
UPDATE bet_outcomes UPDATE bet_outcomes
SET status = $1 SET status = $1
@ -675,6 +825,54 @@ func (q *Queries) UpdateBetOutcomeStatusForEvent(ctx context.Context, arg Update
return items, nil return items, nil
} }
const UpdateBetOutcomeStatusForOddID = `-- name: UpdateBetOutcomeStatusForOddID :many
UPDATE bet_outcomes
SEt status = $1
WHERE odd_id = $2
RETURNING id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires
`
type UpdateBetOutcomeStatusForOddIDParams struct {
Status int32 `json:"status"`
OddID int64 `json:"odd_id"`
}
func (q *Queries) UpdateBetOutcomeStatusForOddID(ctx context.Context, arg UpdateBetOutcomeStatusForOddIDParams) ([]BetOutcome, error) {
rows, err := q.db.Query(ctx, UpdateBetOutcomeStatusForOddID, arg.Status, arg.OddID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []BetOutcome
for rows.Next() {
var i BetOutcome
if err := rows.Scan(
&i.ID,
&i.BetID,
&i.SportID,
&i.EventID,
&i.OddID,
&i.HomeTeamName,
&i.AwayTeamName,
&i.MarketID,
&i.MarketName,
&i.Odd,
&i.OddName,
&i.OddHeader,
&i.OddHandicap,
&i.Status,
&i.Expires,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateBetWithCashback = `-- name: UpdateBetWithCashback :exec const UpdateBetWithCashback = `-- name: UpdateBetWithCashback :exec
UPDATE bets UPDATE bets
SET processed = $1 SET processed = $1

View File

@ -34,32 +34,37 @@ wHERE (
OR $1 IS NULL OR $1 IS NULL
) )
AND ( AND (
is_shop_bet = $2 company_id = $2
OR $2 IS NULL OR $2 IS NULL
) )
AND ( AND (
cashed_out = $3 is_shop_bet = $3
OR $3 IS NULL OR $3 IS NULL
) )
AND ( AND (
full_name ILIKE '%' || $4 || '%' cashed_out = $4
OR phone_number ILIKE '%' || $4 || '%'
OR $4 IS NULL OR $4 IS NULL
) )
AND ( AND (
created_at > $5 full_name ILIKE '%' || $5 || '%'
OR phone_number ILIKE '%' || $5 || '%'
OR $5 IS NULL OR $5 IS NULL
) )
AND ( AND (
created_at < $6 created_at > $6
OR $6 IS NULL OR $6 IS NULL
) )
AND (
created_at < $7
OR $7 IS NULL
)
GROUP BY DATE(created_at) GROUP BY DATE(created_at)
ORDER BY DATE(created_at) ORDER BY DATE(created_at)
` `
type GetBetStatsParams struct { type GetBetStatsParams struct {
UserID pgtype.Int8 `json:"user_id"` UserID pgtype.Int8 `json:"user_id"`
CompanyID pgtype.Int8 `json:"company_id"`
IsShopBet pgtype.Bool `json:"is_shop_bet"` IsShopBet pgtype.Bool `json:"is_shop_bet"`
CashedOut pgtype.Bool `json:"cashed_out"` CashedOut pgtype.Bool `json:"cashed_out"`
Query pgtype.Text `json:"query"` Query pgtype.Text `json:"query"`
@ -79,6 +84,7 @@ type GetBetStatsRow struct {
func (q *Queries) GetBetStats(ctx context.Context, arg GetBetStatsParams) ([]GetBetStatsRow, error) { func (q *Queries) GetBetStats(ctx context.Context, arg GetBetStatsParams) ([]GetBetStatsRow, error) {
rows, err := q.db.Query(ctx, GetBetStats, rows, err := q.db.Query(ctx, GetBetStats,
arg.UserID, arg.UserID,
arg.CompanyID,
arg.IsShopBet, arg.IsShopBet,
arg.CashedOut, arg.CashedOut,
arg.Query, arg.Query,
@ -143,17 +149,22 @@ wHERE (
OR $1 IS NULL OR $1 IS NULL
) )
AND ( AND (
created_at > $2 company_id = $2
OR $2 IS NULL OR $2 IS NULL
) )
AND ( AND (
created_at < $3 created_at > $3
OR $3 IS NULL OR $3 IS NULL
) )
AND (
created_at < $4
OR $4 IS NULL
)
` `
type GetBetSummaryParams struct { type GetBetSummaryParams struct {
UserID pgtype.Int8 `json:"user_id"` UserID pgtype.Int8 `json:"user_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CreatedBefore pgtype.Timestamp `json:"created_before"` CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"` CreatedAfter pgtype.Timestamp `json:"created_after"`
} }
@ -168,7 +179,12 @@ type GetBetSummaryRow struct {
} }
func (q *Queries) GetBetSummary(ctx context.Context, arg GetBetSummaryParams) (GetBetSummaryRow, error) { func (q *Queries) GetBetSummary(ctx context.Context, arg GetBetSummaryParams) (GetBetSummaryRow, error) {
row := q.db.QueryRow(ctx, GetBetSummary, arg.UserID, arg.CreatedBefore, arg.CreatedAfter) row := q.db.QueryRow(ctx, GetBetSummary,
arg.UserID,
arg.CompanyID,
arg.CreatedBefore,
arg.CreatedAfter,
)
var i GetBetSummaryRow var i GetBetSummaryRow
err := row.Scan( err := row.Scan(
&i.TotalStakes, &i.TotalStakes,
@ -198,13 +214,17 @@ WITH market_counts AS (
OR $1 IS NULL OR $1 IS NULL
) )
AND ( AND (
created_at > $2 company_id = $2
OR $2 IS NULL OR $2 IS NULL
) )
AND ( AND (
created_at < $3 created_at > $3
OR $3 IS NULL OR $3 IS NULL
) )
AND (
created_at < $4
OR $4 IS NULL
)
GROUP BY DATE(b.created_at), GROUP BY DATE(b.created_at),
bo.market_name bo.market_name
) )
@ -216,6 +236,7 @@ WHERE rank = 1
type GetMarketPopularityParams struct { type GetMarketPopularityParams struct {
UserID pgtype.Int8 `json:"user_id"` UserID pgtype.Int8 `json:"user_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CreatedBefore pgtype.Timestamp `json:"created_before"` CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"` CreatedAfter pgtype.Timestamp `json:"created_after"`
} }
@ -226,7 +247,12 @@ type GetMarketPopularityRow struct {
} }
func (q *Queries) GetMarketPopularity(ctx context.Context, arg GetMarketPopularityParams) (GetMarketPopularityRow, error) { func (q *Queries) GetMarketPopularity(ctx context.Context, arg GetMarketPopularityParams) (GetMarketPopularityRow, error) {
row := q.db.QueryRow(ctx, GetMarketPopularity, arg.UserID, arg.CreatedBefore, arg.CreatedAfter) row := q.db.QueryRow(ctx, GetMarketPopularity,
arg.UserID,
arg.CompanyID,
arg.CreatedBefore,
arg.CreatedAfter,
)
var i GetMarketPopularityRow var i GetMarketPopularityRow
err := row.Scan(&i.Date, &i.MarketName) err := row.Scan(&i.Date, &i.MarketName)
return i, err return i, err

View File

@ -455,10 +455,19 @@ const SearchBranchByName = `-- name: SearchBranchByName :many
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active
FROM branch_details FROM branch_details
WHERE name ILIKE '%' || $1 || '%' WHERE name ILIKE '%' || $1 || '%'
AND (
company_id = $2
OR $2 IS NULL
)
` `
func (q *Queries) SearchBranchByName(ctx context.Context, dollar_1 pgtype.Text) ([]BranchDetail, error) { type SearchBranchByNameParams struct {
rows, err := q.db.Query(ctx, SearchBranchByName, dollar_1) Column1 pgtype.Text `json:"column_1"`
CompanyID pgtype.Int8 `json:"company_id"`
}
func (q *Queries) SearchBranchByName(ctx context.Context, arg SearchBranchByNameParams) ([]BranchDetail, error) {
rows, err := q.db.Query(ctx, SearchBranchByName, arg.Column1, arg.CompanyID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,9 +17,10 @@ INSERT INTO companies (
slug, slug,
admin_id, admin_id,
wallet_id, wallet_id,
deducted_percentage deducted_percentage,
is_active
) )
VALUES ($1, $2, $3, $4, $5) VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at RETURNING id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at
` `
@ -29,6 +30,7 @@ type CreateCompanyParams struct {
AdminID int64 `json:"admin_id"` AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"` WalletID int64 `json:"wallet_id"`
DeductedPercentage float32 `json:"deducted_percentage"` DeductedPercentage float32 `json:"deducted_percentage"`
IsActive bool `json:"is_active"`
} }
func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (Company, error) { func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (Company, error) {
@ -38,6 +40,7 @@ func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (C
arg.AdminID, arg.AdminID,
arg.WalletID, arg.WalletID,
arg.DeductedPercentage, arg.DeductedPercentage,
arg.IsActive,
) )
var i Company var i Company
err := row.Scan( err := row.Scan(
@ -153,17 +156,27 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail
return i, err return i, err
} }
const GetCompanyIDUsingSlug = `-- name: GetCompanyIDUsingSlug :one const GetCompanyUsingSlug = `-- name: GetCompanyUsingSlug :one
SELECT id SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at
FROM companies FROM companies
WHERE slug = $1 WHERE slug = $1
` `
func (q *Queries) GetCompanyIDUsingSlug(ctx context.Context, slug string) (int64, error) { func (q *Queries) GetCompanyUsingSlug(ctx context.Context, slug string) (Company, error) {
row := q.db.QueryRow(ctx, GetCompanyIDUsingSlug, slug) row := q.db.QueryRow(ctx, GetCompanyUsingSlug, slug)
var id int64 var i Company
err := row.Scan(&id) err := row.Scan(
return id, err &i.ID,
&i.Name,
&i.Slug,
&i.AdminID,
&i.WalletID,
&i.DeductedPercentage,
&i.IsActive,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
} }
const SearchCompanyByName = `-- name: SearchCompanyByName :many const SearchCompanyByName = `-- name: SearchCompanyByName :many
@ -207,7 +220,7 @@ func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text)
return items, nil return items, nil
} }
const UpdateCompany = `-- name: UpdateCompany :one const UpdateCompany = `-- name: UpdateCompany :exec
UPDATE companies UPDATE companies
SET name = COALESCE($2, name), SET name = COALESCE($2, name),
admin_id = COALESCE($3, admin_id), admin_id = COALESCE($3, admin_id),
@ -216,9 +229,9 @@ SET name = COALESCE($2, name),
$5, $5,
deducted_percentage deducted_percentage
), ),
slug = COALESCE($6, slug),
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
WHERE id = $1 WHERE id = $1
RETURNING id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at
` `
type UpdateCompanyParams struct { type UpdateCompanyParams struct {
@ -227,27 +240,17 @@ type UpdateCompanyParams struct {
AdminID pgtype.Int8 `json:"admin_id"` AdminID pgtype.Int8 `json:"admin_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
DeductedPercentage pgtype.Float4 `json:"deducted_percentage"` DeductedPercentage pgtype.Float4 `json:"deducted_percentage"`
Slug pgtype.Text `json:"slug"`
} }
func (q *Queries) UpdateCompany(ctx context.Context, arg UpdateCompanyParams) (Company, error) { func (q *Queries) UpdateCompany(ctx context.Context, arg UpdateCompanyParams) error {
row := q.db.QueryRow(ctx, UpdateCompany, _, err := q.db.Exec(ctx, UpdateCompany,
arg.ID, arg.ID,
arg.Name, arg.Name,
arg.AdminID, arg.AdminID,
arg.IsActive, arg.IsActive,
arg.DeductedPercentage, arg.DeductedPercentage,
arg.Slug,
) )
var i Company return err
err := row.Scan(
&i.ID,
&i.Name,
&i.Slug,
&i.AdminID,
&i.WalletID,
&i.DeductedPercentage,
&i.IsActive,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
} }

View File

@ -643,7 +643,8 @@ INSERT INTO enetpulse_tournament_stages (
updates_count, updates_count,
last_updated_at, last_updated_at,
status status
) VALUES ( )
VALUES (
$1, -- stage_id $1, -- stage_id
$2, -- name $2, -- name
$3, -- tournament_fk $3, -- tournament_fk
@ -656,6 +657,19 @@ INSERT INTO enetpulse_tournament_stages (
$10, -- last_updated_at $10, -- last_updated_at
$11 -- status $11 -- status
) )
ON CONFLICT (stage_id) DO UPDATE
SET
name = EXCLUDED.name,
tournament_fk = EXCLUDED.tournament_fk,
gender = EXCLUDED.gender,
country_fk = EXCLUDED.country_fk,
country_name = EXCLUDED.country_name,
start_date = EXCLUDED.start_date,
end_date = EXCLUDED.end_date,
updates_count = EXCLUDED.updates_count,
last_updated_at = EXCLUDED.last_updated_at,
status = EXCLUDED.status,
updated_at = NOW()
RETURNING id, stage_id, name, tournament_fk, gender, country_fk, country_name, start_date, end_date, updates_count, last_updated_at, status, created_at, updated_at RETURNING id, stage_id, name, tournament_fk, gender, country_fk, country_name, start_date, end_date, updates_count, last_updated_at, status, created_at, updated_at
` `
@ -1158,6 +1172,134 @@ func (q *Queries) GetAllEnetpulseTournaments(ctx context.Context) ([]EnetpulseTo
return items, nil return items, nil
} }
const GetFixturesWithPreodds = `-- name: GetFixturesWithPreodds :many
SELECT
f.fixture_id AS id,
f.fixture_id AS fixture_id,
f.name AS fixture_name,
f.sport_fk,
f.tournament_fk,
f.tournament_template_fk,
f.tournament_stage_fk,
f.start_date,
f.status_type,
f.status_desc_fk,
f.round_type_fk,
f.updates_count AS fixture_updates_count,
f.last_updated_at AS fixture_last_updated_at,
f.created_at AS fixture_created_at,
f.updated_at AS fixture_updated_at,
-- Preodds fields
p.id AS preodds_db_id,
p.preodds_id,
p.event_fk,
p.outcome_type_fk,
p.outcome_scope_fk,
p.outcome_subtype_fk,
p.event_participant_number,
p.iparam,
p.iparam2,
p.dparam,
p.dparam2,
p.sparam,
p.updates_count AS preodds_updates_count,
p.last_updated_at AS preodds_last_updated_at,
p.created_at AS preodds_created_at,
p.updated_at AS preodds_updated_at
FROM enetpulse_fixtures f
LEFT JOIN enetpulse_preodds p
ON p.event_fk = f.id
ORDER BY f.start_date DESC
`
type GetFixturesWithPreoddsRow struct {
ID string `json:"id"`
FixtureID string `json:"fixture_id"`
FixtureName string `json:"fixture_name"`
SportFk string `json:"sport_fk"`
TournamentFk pgtype.Text `json:"tournament_fk"`
TournamentTemplateFk pgtype.Text `json:"tournament_template_fk"`
TournamentStageFk pgtype.Text `json:"tournament_stage_fk"`
StartDate pgtype.Timestamptz `json:"start_date"`
StatusType pgtype.Text `json:"status_type"`
StatusDescFk pgtype.Text `json:"status_desc_fk"`
RoundTypeFk pgtype.Text `json:"round_type_fk"`
FixtureUpdatesCount pgtype.Int4 `json:"fixture_updates_count"`
FixtureLastUpdatedAt pgtype.Timestamptz `json:"fixture_last_updated_at"`
FixtureCreatedAt pgtype.Timestamptz `json:"fixture_created_at"`
FixtureUpdatedAt pgtype.Timestamptz `json:"fixture_updated_at"`
PreoddsDbID pgtype.Int8 `json:"preodds_db_id"`
PreoddsID pgtype.Text `json:"preodds_id"`
EventFk pgtype.Int8 `json:"event_fk"`
OutcomeTypeFk pgtype.Int4 `json:"outcome_type_fk"`
OutcomeScopeFk pgtype.Int4 `json:"outcome_scope_fk"`
OutcomeSubtypeFk pgtype.Int4 `json:"outcome_subtype_fk"`
EventParticipantNumber pgtype.Int4 `json:"event_participant_number"`
Iparam pgtype.Text `json:"iparam"`
Iparam2 pgtype.Text `json:"iparam2"`
Dparam pgtype.Text `json:"dparam"`
Dparam2 pgtype.Text `json:"dparam2"`
Sparam pgtype.Text `json:"sparam"`
PreoddsUpdatesCount pgtype.Int4 `json:"preodds_updates_count"`
PreoddsLastUpdatedAt pgtype.Timestamptz `json:"preodds_last_updated_at"`
PreoddsCreatedAt pgtype.Timestamptz `json:"preodds_created_at"`
PreoddsUpdatedAt pgtype.Timestamptz `json:"preodds_updated_at"`
}
func (q *Queries) GetFixturesWithPreodds(ctx context.Context) ([]GetFixturesWithPreoddsRow, error) {
rows, err := q.db.Query(ctx, GetFixturesWithPreodds)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetFixturesWithPreoddsRow
for rows.Next() {
var i GetFixturesWithPreoddsRow
if err := rows.Scan(
&i.ID,
&i.FixtureID,
&i.FixtureName,
&i.SportFk,
&i.TournamentFk,
&i.TournamentTemplateFk,
&i.TournamentStageFk,
&i.StartDate,
&i.StatusType,
&i.StatusDescFk,
&i.RoundTypeFk,
&i.FixtureUpdatesCount,
&i.FixtureLastUpdatedAt,
&i.FixtureCreatedAt,
&i.FixtureUpdatedAt,
&i.PreoddsDbID,
&i.PreoddsID,
&i.EventFk,
&i.OutcomeTypeFk,
&i.OutcomeScopeFk,
&i.OutcomeSubtypeFk,
&i.EventParticipantNumber,
&i.Iparam,
&i.Iparam2,
&i.Dparam,
&i.Dparam2,
&i.Sparam,
&i.PreoddsUpdatesCount,
&i.PreoddsLastUpdatedAt,
&i.PreoddsCreatedAt,
&i.PreoddsUpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetTournamentStagesByTournamentFK = `-- name: GetTournamentStagesByTournamentFK :many const GetTournamentStagesByTournamentFK = `-- name: GetTournamentStagesByTournamentFK :many
SELECT id, stage_id, name, tournament_fk, gender, country_fk, country_name, start_date, end_date, updates_count, last_updated_at, status, created_at, updated_at SELECT id, stage_id, name, tournament_fk, gender, country_fk, country_name, start_date, end_date, updates_count, last_updated_at, status, created_at, updated_at
FROM enetpulse_tournament_stages FROM enetpulse_tournament_stages

View File

@ -22,7 +22,7 @@ func (q *Queries) DeleteEvent(ctx context.Context, id int64) error {
} }
const GetAllEvents = `-- name: GetAllEvents :many const GetAllEvents = `-- name: GetAllEvents :many
SELECT id, source_event_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, league_cc SELECT id, source_event_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, updated_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc, total_outcomes
FROM event_with_country FROM event_with_country
WHERE ( WHERE (
is_live = $1 is_live = $1
@ -122,12 +122,14 @@ func (q *Queries) GetAllEvents(ctx context.Context, arg GetAllEventsParams) ([]E
&i.IsLive, &i.IsLive,
&i.Status, &i.Status,
&i.FetchedAt, &i.FetchedAt,
&i.UpdatedAt,
&i.Source, &i.Source,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.DefaultIsFeatured, &i.DefaultIsFeatured,
&i.DefaultWinningUpperLimit, &i.DefaultWinningUpperLimit,
&i.IsMonitored, &i.IsMonitored,
&i.LeagueCc, &i.LeagueCc,
&i.TotalOutcomes,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -140,7 +142,7 @@ func (q *Queries) GetAllEvents(ctx context.Context, arg GetAllEventsParams) ([]E
} }
const GetEventByID = `-- name: GetEventByID :one const GetEventByID = `-- name: GetEventByID :one
SELECT id, source_event_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, league_cc SELECT id, source_event_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, updated_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc, total_outcomes
FROM event_with_country FROM event_with_country
WHERE id = $1 WHERE id = $1
LIMIT 1 LIMIT 1
@ -171,18 +173,20 @@ func (q *Queries) GetEventByID(ctx context.Context, id int64) (EventWithCountry,
&i.IsLive, &i.IsLive,
&i.Status, &i.Status,
&i.FetchedAt, &i.FetchedAt,
&i.UpdatedAt,
&i.Source, &i.Source,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.DefaultIsFeatured, &i.DefaultIsFeatured,
&i.DefaultWinningUpperLimit, &i.DefaultWinningUpperLimit,
&i.IsMonitored, &i.IsMonitored,
&i.LeagueCc, &i.LeagueCc,
&i.TotalOutcomes,
) )
return i, err return i, err
} }
const GetEventBySourceID = `-- name: GetEventBySourceID :one const GetEventBySourceID = `-- name: GetEventBySourceID :one
SELECT id, source_event_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, league_cc SELECT id, source_event_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, updated_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc, total_outcomes
FROM event_with_country FROM event_with_country
WHERE source_event_id = $1 WHERE source_event_id = $1
AND source = $2 AND source = $2
@ -218,18 +222,20 @@ func (q *Queries) GetEventBySourceID(ctx context.Context, arg GetEventBySourceID
&i.IsLive, &i.IsLive,
&i.Status, &i.Status,
&i.FetchedAt, &i.FetchedAt,
&i.UpdatedAt,
&i.Source, &i.Source,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.DefaultIsFeatured, &i.DefaultIsFeatured,
&i.DefaultWinningUpperLimit, &i.DefaultWinningUpperLimit,
&i.IsMonitored, &i.IsMonitored,
&i.LeagueCc, &i.LeagueCc,
&i.TotalOutcomes,
) )
return i, err return i, err
} }
const GetEventWithSettingByID = `-- name: GetEventWithSettingByID :one const GetEventWithSettingByID = `-- name: GetEventWithSettingByID :one
SELECT e.id, e.source_event_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, SELECT e.id, e.source_event_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.updated_at, e.source, e.default_is_active, e.default_is_featured, e.default_winning_upper_limit, e.is_monitored,
ces.company_id, ces.company_id,
COALESCE(ces.is_active, e.default_is_active) AS is_active, COALESCE(ces.is_active, e.default_is_active) AS is_active,
COALESCE(ces.is_featured, e.default_is_featured) AS is_featured, COALESCE(ces.is_featured, e.default_is_featured) AS is_featured,
@ -238,11 +244,18 @@ SELECT e.id, e.source_event_id, e.sport_id, e.match_name, e.home_team, e.away_te
e.default_winning_upper_limit e.default_winning_upper_limit
) AS winning_upper_limit, ) AS winning_upper_limit,
ces.updated_at, ces.updated_at,
l.country_code as league_cc l.country_code as league_cc,
COALESCE(om.total_outcomes, 0) AS total_outcomes
FROM events e FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $2 AND ces.company_id = $2
JOIN leagues l ON l.id = e.league_id JOIN leagues l ON l.id = e.league_id
LEFT JOIN (
SELECT event_id,
SUM(number_of_outcomes) AS total_outcomes
FROM odds_market
GROUP BY event_id
) om ON om.event_id = e.id
WHERE e.id = $1 WHERE e.id = $1
LIMIT 1 LIMIT 1
` `
@ -274,6 +287,7 @@ type GetEventWithSettingByIDRow struct {
IsLive bool `json:"is_live"` IsLive bool `json:"is_live"`
Status string `json:"status"` Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Source string `json:"source"` Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"` DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
@ -282,9 +296,10 @@ type GetEventWithSettingByIDRow struct {
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"` IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"` WinningUpperLimit int64 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt_2 pgtype.Timestamp `json:"updated_at_2"`
LeagueCc pgtype.Text `json:"league_cc"` LeagueCc pgtype.Text `json:"league_cc"`
TotalOutcomes int64 `json:"total_outcomes"`
} }
func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithSettingByIDParams) (GetEventWithSettingByIDRow, error) { func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithSettingByIDParams) (GetEventWithSettingByIDRow, error) {
@ -312,6 +327,7 @@ func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithS
&i.IsLive, &i.IsLive,
&i.Status, &i.Status,
&i.FetchedAt, &i.FetchedAt,
&i.UpdatedAt,
&i.Source, &i.Source,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.DefaultIsFeatured, &i.DefaultIsFeatured,
@ -321,14 +337,15 @@ func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithS
&i.IsActive, &i.IsActive,
&i.IsFeatured, &i.IsFeatured,
&i.WinningUpperLimit, &i.WinningUpperLimit,
&i.UpdatedAt, &i.UpdatedAt_2,
&i.LeagueCc, &i.LeagueCc,
&i.TotalOutcomes,
) )
return i, err return i, err
} }
const GetEventsWithSettings = `-- name: GetEventsWithSettings :many const GetEventsWithSettings = `-- name: GetEventsWithSettings :many
SELECT e.id, e.source_event_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, SELECT e.id, e.source_event_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.updated_at, e.source, e.default_is_active, e.default_is_featured, e.default_winning_upper_limit, e.is_monitored,
ces.company_id, ces.company_id,
COALESCE(ces.is_active, e.default_is_active) AS is_active, COALESCE(ces.is_active, e.default_is_active) AS is_active,
COALESCE(ces.is_featured, e.default_is_featured) AS is_featured, COALESCE(ces.is_featured, e.default_is_featured) AS is_featured,
@ -337,11 +354,18 @@ SELECT e.id, e.source_event_id, e.sport_id, e.match_name, e.home_team, e.away_te
e.default_winning_upper_limit e.default_winning_upper_limit
) AS winning_upper_limit, ) AS winning_upper_limit,
ces.updated_at, ces.updated_at,
l.country_code as league_cc l.country_code as league_cc,
COALESCE(om.total_outcomes, 0) AS total_outcomes
FROM events e FROM events e
LEFT JOIN company_event_settings ces ON e.id = ces.event_id LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $1 AND ces.company_id = $1
JOIN leagues l ON l.id = e.league_id JOIN leagues l ON l.id = e.league_id
LEFT JOIN (
SELECT event_id,
SUM(number_of_outcomes) AS total_outcomes
FROM odds_market
GROUP BY event_id
) om ON om.event_id = e.id
WHERE ( WHERE (
is_live = $2 is_live = $2
OR $2 IS NULL OR $2 IS NULL
@ -432,6 +456,7 @@ type GetEventsWithSettingsRow struct {
IsLive bool `json:"is_live"` IsLive bool `json:"is_live"`
Status string `json:"status"` Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Source string `json:"source"` Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"` DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
@ -440,9 +465,10 @@ type GetEventsWithSettingsRow struct {
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"` IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"` WinningUpperLimit int64 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt_2 pgtype.Timestamp `json:"updated_at_2"`
LeagueCc pgtype.Text `json:"league_cc"` LeagueCc pgtype.Text `json:"league_cc"`
TotalOutcomes int64 `json:"total_outcomes"`
} }
func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSettingsParams) ([]GetEventsWithSettingsRow, error) { func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSettingsParams) ([]GetEventsWithSettingsRow, error) {
@ -491,6 +517,7 @@ func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSe
&i.IsLive, &i.IsLive,
&i.Status, &i.Status,
&i.FetchedAt, &i.FetchedAt,
&i.UpdatedAt,
&i.Source, &i.Source,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.DefaultIsFeatured, &i.DefaultIsFeatured,
@ -500,8 +527,9 @@ func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSe
&i.IsActive, &i.IsActive,
&i.IsFeatured, &i.IsFeatured,
&i.WinningUpperLimit, &i.WinningUpperLimit,
&i.UpdatedAt, &i.UpdatedAt_2,
&i.LeagueCc, &i.LeagueCc,
&i.TotalOutcomes,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -514,7 +542,9 @@ func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSe
} }
const GetSportAndLeagueIDs = `-- name: GetSportAndLeagueIDs :one const GetSportAndLeagueIDs = `-- name: GetSportAndLeagueIDs :one
SELECT sport_id, league_id FROM events SELECT sport_id,
league_id
FROM events
WHERE id = $1 WHERE id = $1
` `
@ -831,7 +861,7 @@ func (q *Queries) ListLiveEvents(ctx context.Context) ([]int64, error) {
return items, nil return items, nil
} }
const SaveEventSettings = `-- name: SaveEventSettings :exec const SaveTenantEventSettings = `-- name: SaveTenantEventSettings :exec
INSERT INTO company_event_settings ( INSERT INTO company_event_settings (
company_id, company_id,
event_id, event_id,
@ -846,16 +876,16 @@ SET is_active = EXCLUDED.is_active,
winning_upper_limit = EXCLUDED.winning_upper_limit winning_upper_limit = EXCLUDED.winning_upper_limit
` `
type SaveEventSettingsParams struct { type SaveTenantEventSettingsParams struct {
CompanyID int64 `json:"company_id"` CompanyID int64 `json:"company_id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"` IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"` WinningUpperLimit pgtype.Int8 `json:"winning_upper_limit"`
} }
func (q *Queries) SaveEventSettings(ctx context.Context, arg SaveEventSettingsParams) error { func (q *Queries) SaveTenantEventSettings(ctx context.Context, arg SaveTenantEventSettingsParams) error {
_, err := q.db.Exec(ctx, SaveEventSettings, _, err := q.db.Exec(ctx, SaveTenantEventSettings,
arg.CompanyID, arg.CompanyID,
arg.EventID, arg.EventID,
arg.IsActive, arg.IsActive,
@ -867,7 +897,8 @@ func (q *Queries) SaveEventSettings(ctx context.Context, arg SaveEventSettingsPa
const UpdateEventMonitored = `-- name: UpdateEventMonitored :exec const UpdateEventMonitored = `-- name: UpdateEventMonitored :exec
UPDATE events UPDATE events
SET is_monitored = $1 SET is_monitored = $1,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2 WHERE id = $2
` `
@ -881,6 +912,38 @@ func (q *Queries) UpdateEventMonitored(ctx context.Context, arg UpdateEventMonit
return err return err
} }
const UpdateGlobalEventSettings = `-- name: UpdateGlobalEventSettings :exec
UPDATE events
SET default_is_active = COALESCE($2, default_is_active),
default_is_featured = COALESCE(
$3,
default_is_featured
),
default_winning_upper_limit = COALESCE(
$4,
default_winning_upper_limit
),
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`
type UpdateGlobalEventSettingsParams struct {
ID int64 `json:"id"`
DefaultIsActive pgtype.Bool `json:"default_is_active"`
DefaultIsFeatured pgtype.Bool `json:"default_is_featured"`
DefaultWinningUpperLimit pgtype.Int8 `json:"default_winning_upper_limit"`
}
func (q *Queries) UpdateGlobalEventSettings(ctx context.Context, arg UpdateGlobalEventSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateGlobalEventSettings,
arg.ID,
arg.DefaultIsActive,
arg.DefaultIsFeatured,
arg.DefaultWinningUpperLimit,
)
return err
}
const UpdateMatchResult = `-- name: UpdateMatchResult :exec const UpdateMatchResult = `-- name: UpdateMatchResult :exec
UPDATE events UPDATE events
SET score = $1, SET score = $1,

View File

@ -48,14 +48,19 @@ WHERE (
name ILIKE '%' || $3 || '%' name ILIKE '%' || $3 || '%'
OR $3 IS NULL OR $3 IS NULL
) )
AND (
default_is_active = $4
OR $4 IS NULL
)
ORDER BY name ASC ORDER BY name ASC
LIMIT $5 OFFSET $4 LIMIT $6 OFFSET $5
` `
type GetAllLeaguesParams struct { type GetAllLeaguesParams struct {
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
SportID pgtype.Int4 `json:"sport_id"` SportID pgtype.Int4 `json:"sport_id"`
Query pgtype.Text `json:"query"` Query pgtype.Text `json:"query"`
IsActive pgtype.Bool `json:"is_active"`
Offset pgtype.Int4 `json:"offset"` Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"` Limit pgtype.Int4 `json:"limit"`
} }
@ -65,6 +70,7 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
arg.CountryCode, arg.CountryCode,
arg.SportID, arg.SportID,
arg.Query, arg.Query,
arg.IsActive,
arg.Offset, arg.Offset,
arg.Limit, arg.Limit,
) )
@ -199,6 +205,46 @@ func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeagu
return items, nil return items, nil
} }
const GetTotalLeagues = `-- name: GetTotalLeagues :one
SELECT COUNT(*)
FROM leagues
WHERE (
country_code = $1
OR $1 IS NULL
)
AND (
sport_id = $2
OR $2 IS NULL
)
AND (
name ILIKE '%' || $3 || '%'
OR $3 IS NULL
)
AND (
default_is_active = $4
OR $4 IS NULL
)
`
type GetTotalLeaguesParams struct {
CountryCode pgtype.Text `json:"country_code"`
SportID pgtype.Int4 `json:"sport_id"`
Query pgtype.Text `json:"query"`
IsActive pgtype.Bool `json:"is_active"`
}
func (q *Queries) GetTotalLeagues(ctx context.Context, arg GetTotalLeaguesParams) (int64, error) {
row := q.db.QueryRow(ctx, GetTotalLeagues,
arg.CountryCode,
arg.SportID,
arg.Query,
arg.IsActive,
)
var count int64
err := row.Scan(&count)
return count, err
}
const GetTotalLeaguesWithSettings = `-- name: GetTotalLeaguesWithSettings :one const GetTotalLeaguesWithSettings = `-- name: GetTotalLeaguesWithSettings :one
SELECT COUNT(*) SELECT COUNT(*)
FROM leagues l FROM leagues l
@ -292,7 +338,7 @@ func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) erro
return err return err
} }
const InsertLeagueSettings = `-- name: InsertLeagueSettings :exec const SaveLeagueSettings = `-- name: SaveLeagueSettings :exec
INSERT INTO company_league_settings ( INSERT INTO company_league_settings (
company_id, company_id,
league_id, league_id,
@ -305,15 +351,15 @@ SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured is_featured = EXCLUDED.is_featured
` `
type InsertLeagueSettingsParams struct { type SaveLeagueSettingsParams struct {
CompanyID int64 `json:"company_id"` CompanyID int64 `json:"company_id"`
LeagueID int64 `json:"league_id"` LeagueID int64 `json:"league_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"` IsFeatured pgtype.Bool `json:"is_featured"`
} }
func (q *Queries) InsertLeagueSettings(ctx context.Context, arg InsertLeagueSettingsParams) error { func (q *Queries) SaveLeagueSettings(ctx context.Context, arg SaveLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, InsertLeagueSettings, _, err := q.db.Exec(ctx, SaveLeagueSettings,
arg.CompanyID, arg.CompanyID,
arg.LeagueID, arg.LeagueID,
arg.IsActive, arg.IsActive,
@ -322,6 +368,52 @@ func (q *Queries) InsertLeagueSettings(ctx context.Context, arg InsertLeagueSett
return err return err
} }
const UpdateCompanyLeagueSettings = `-- name: UpdateCompanyLeagueSettings :exec
UPDATE company_league_settings
SET is_active = COALESCE($3, is_active),
is_featured = COALESCE(
$4,
is_featured
)
WHERE league_id = $1
AND company_id = $2
`
type UpdateCompanyLeagueSettingsParams struct {
LeagueID int64 `json:"league_id"`
CompanyID int64 `json:"company_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) UpdateCompanyLeagueSettings(ctx context.Context, arg UpdateCompanyLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateCompanyLeagueSettings,
arg.LeagueID,
arg.CompanyID,
arg.IsActive,
arg.IsFeatured,
)
return err
}
const UpdateGlobalLeagueSettings = `-- name: UpdateGlobalLeagueSettings :exec
UPDATE leagues
SET default_is_active = COALESCE($2, default_is_active),
default_is_featured = COALESCE($3, default_is_featured)
WHERE id = $1
`
type UpdateGlobalLeagueSettingsParams struct {
ID int64 `json:"id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) UpdateGlobalLeagueSettings(ctx context.Context, arg UpdateGlobalLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateGlobalLeagueSettings, arg.ID, arg.IsActive, arg.IsFeatured)
return err
}
const UpdateLeague = `-- name: UpdateLeague :exec const UpdateLeague = `-- name: UpdateLeague :exec
UPDATE leagues UPDATE leagues
SET name = COALESCE($2, name), SET name = COALESCE($2, name),
@ -349,31 +441,3 @@ func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) erro
) )
return err return err
} }
const UpdateLeagueSettings = `-- name: UpdateLeagueSettings :exec
UPDATE company_league_settings
SET is_active = COALESCE($3, is_active),
is_featured = COALESCE(
$4,
is_featured
)
WHERE league_id = $1
AND company_id = $2
`
type UpdateLeagueSettingsParams struct {
LeagueID int64 `json:"league_id"`
CompanyID int64 `json:"company_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) UpdateLeagueSettings(ctx context.Context, arg UpdateLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateLeagueSettings,
arg.LeagueID,
arg.CompanyID,
arg.IsActive,
arg.IsFeatured,
)
return err
}

View File

@ -82,6 +82,7 @@ type BetWithOutcome struct {
FullName interface{} `json:"full_name"` FullName interface{} `json:"full_name"`
PhoneNumber pgtype.Text `json:"phone_number"` PhoneNumber pgtype.Text `json:"phone_number"`
Outcomes []BetOutcome `json:"outcomes"` Outcomes []BetOutcome `json:"outcomes"`
CompanySlug string `json:"company_slug"`
} }
type Branch struct { type Branch struct {
@ -177,7 +178,7 @@ type CompanyEventSetting struct {
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"` IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"` WinningUpperLimit pgtype.Int8 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
@ -468,6 +469,7 @@ type Event struct {
IsLive bool `json:"is_live"` IsLive bool `json:"is_live"`
Status string `json:"status"` Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Source string `json:"source"` Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"` DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
@ -504,12 +506,14 @@ type EventWithCountry struct {
IsLive bool `json:"is_live"` IsLive bool `json:"is_live"`
Status string `json:"status"` Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Source string `json:"source"` Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"` DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"` IsMonitored bool `json:"is_monitored"`
LeagueCc pgtype.Text `json:"league_cc"` LeagueCc pgtype.Text `json:"league_cc"`
TotalOutcomes int64 `json:"total_outcomes"`
} }
type EventWithSetting struct { type EventWithSetting struct {
@ -534,6 +538,7 @@ type EventWithSetting struct {
IsLive bool `json:"is_live"` IsLive bool `json:"is_live"`
Status string `json:"status"` Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Source string `json:"source"` Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"` DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
@ -542,9 +547,10 @@ type EventWithSetting struct {
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"` IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"` WinningUpperLimit int64 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` CompanyUpdatedAt pgtype.Timestamp `json:"company_updated_at"`
LeagueCc pgtype.Text `json:"league_cc"` LeagueCc pgtype.Text `json:"league_cc"`
TotalOutcomes int64 `json:"total_outcomes"`
} }
type ExchangeRate struct { type ExchangeRate struct {
@ -619,6 +625,8 @@ type Notification struct {
Priority pgtype.Int4 `json:"priority"` Priority pgtype.Int4 `json:"priority"`
Version int32 `json:"version"` Version int32 `json:"version"`
Timestamp pgtype.Timestamptz `json:"timestamp"` Timestamp pgtype.Timestamptz `json:"timestamp"`
Img pgtype.Text `json:"img"`
Expires pgtype.Timestamptz `json:"expires"`
Metadata []byte `json:"metadata"` Metadata []byte `json:"metadata"`
} }
@ -633,49 +641,52 @@ type OddHistory struct {
} }
type OddsMarket struct { type OddsMarket struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
RawOdds []byte `json:"raw_odds"` RawOdds []byte `json:"raw_odds"`
DefaultIsActive bool `json:"default_is_active"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` DefaultIsActive bool `json:"default_is_active"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
} }
type OddsMarketWithEvent struct { type OddsMarketWithEvent struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
RawOdds []byte `json:"raw_odds"` RawOdds []byte `json:"raw_odds"`
DefaultIsActive bool `json:"default_is_active"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` DefaultIsActive bool `json:"default_is_active"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
IsMonitored bool `json:"is_monitored"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
IsLive bool `json:"is_live"` IsMonitored bool `json:"is_monitored"`
Status string `json:"status"` IsLive bool `json:"is_live"`
Source string `json:"source"` Status string `json:"status"`
Source string `json:"source"`
} }
type OddsMarketWithSetting struct { type OddsMarketWithSetting struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` DefaultIsActive bool `json:"default_is_active"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
CompanyID pgtype.Int8 `json:"company_id"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
IsActive bool `json:"is_active"` CompanyID pgtype.Int8 `json:"company_id"`
RawOdds []byte `json:"raw_odds"` IsActive bool `json:"is_active"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
type Otp struct { type Otp struct {
@ -691,13 +702,14 @@ type Otp struct {
} }
type Raffle struct { type Raffle struct {
ID int32 `json:"id"` ID int32 `json:"id"`
CompanyID int32 `json:"company_id"` CompanyID int32 `json:"company_id"`
Name string `json:"name"` Name string `json:"name"`
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
Type string `json:"type"` TicketLimit int32 `json:"ticket_limit"`
Status string `json:"status"` Type string `json:"type"`
Status string `json:"status"`
} }
type RaffleGameFilter struct { type RaffleGameFilter struct {
@ -824,6 +836,7 @@ type ShopBetDetail struct {
TransactionVerified bool `json:"transaction_verified"` TransactionVerified bool `json:"transaction_verified"`
Status int32 `json:"status"` Status int32 `json:"status"`
TotalOdds float32 `json:"total_odds"` TotalOdds float32 `json:"total_odds"`
FastCode string `json:"fast_code"`
Outcomes []BetOutcome `json:"outcomes"` Outcomes []BetOutcome `json:"outcomes"`
} }

View File

@ -39,6 +39,8 @@ INSERT INTO notifications (
payload, payload,
priority, priority,
timestamp, timestamp,
expires,
img,
metadata metadata
) )
VALUES ( VALUES (
@ -54,9 +56,11 @@ VALUES (
$10, $10,
$11, $11,
$12, $12,
$13 $13,
$14,
$15
) )
RETURNING id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, metadata RETURNING id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata
` `
type CreateNotificationParams struct { type CreateNotificationParams struct {
@ -72,6 +76,8 @@ type CreateNotificationParams struct {
Payload []byte `json:"payload"` Payload []byte `json:"payload"`
Priority pgtype.Int4 `json:"priority"` Priority pgtype.Int4 `json:"priority"`
Timestamp pgtype.Timestamptz `json:"timestamp"` Timestamp pgtype.Timestamptz `json:"timestamp"`
Expires pgtype.Timestamptz `json:"expires"`
Img pgtype.Text `json:"img"`
Metadata []byte `json:"metadata"` Metadata []byte `json:"metadata"`
} }
@ -89,6 +95,8 @@ func (q *Queries) CreateNotification(ctx context.Context, arg CreateNotification
arg.Payload, arg.Payload,
arg.Priority, arg.Priority,
arg.Timestamp, arg.Timestamp,
arg.Expires,
arg.Img,
arg.Metadata, arg.Metadata,
) )
var i Notification var i Notification
@ -106,13 +114,25 @@ func (q *Queries) CreateNotification(ctx context.Context, arg CreateNotification
&i.Priority, &i.Priority,
&i.Version, &i.Version,
&i.Timestamp, &i.Timestamp,
&i.Img,
&i.Expires,
&i.Metadata, &i.Metadata,
) )
return i, err return i, err
} }
const DeleteOldNotifications = `-- name: DeleteOldNotifications :exec
DELETE FROM notifications
WHERE expires < now()
`
func (q *Queries) DeleteOldNotifications(ctx context.Context) error {
_, err := q.db.Exec(ctx, DeleteOldNotifications)
return err
}
const GetAllNotifications = `-- name: GetAllNotifications :many const GetAllNotifications = `-- name: GetAllNotifications :many
SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, metadata SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata
FROM notifications FROM notifications
ORDER BY timestamp DESC ORDER BY timestamp DESC
LIMIT $1 OFFSET $2 LIMIT $1 OFFSET $2
@ -146,6 +166,8 @@ func (q *Queries) GetAllNotifications(ctx context.Context, arg GetAllNotificatio
&i.Priority, &i.Priority,
&i.Version, &i.Version,
&i.Timestamp, &i.Timestamp,
&i.Img,
&i.Expires,
&i.Metadata, &i.Metadata,
); err != nil { ); err != nil {
return nil, err return nil, err
@ -159,7 +181,7 @@ func (q *Queries) GetAllNotifications(ctx context.Context, arg GetAllNotificatio
} }
const GetNotification = `-- name: GetNotification :one const GetNotification = `-- name: GetNotification :one
SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, metadata SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata
FROM notifications FROM notifications
WHERE id = $1 WHERE id = $1
LIMIT 1 LIMIT 1
@ -182,6 +204,8 @@ func (q *Queries) GetNotification(ctx context.Context, id string) (Notification,
&i.Priority, &i.Priority,
&i.Version, &i.Version,
&i.Timestamp, &i.Timestamp,
&i.Img,
&i.Expires,
&i.Metadata, &i.Metadata,
) )
return i, err return i, err
@ -254,7 +278,7 @@ func (q *Queries) GetUserNotificationCount(ctx context.Context, recipientID int6
} }
const GetUserNotifications = `-- name: GetUserNotifications :many const GetUserNotifications = `-- name: GetUserNotifications :many
SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, metadata SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata
FROM notifications FROM notifications
WHERE recipient_id = $1 WHERE recipient_id = $1
ORDER BY timestamp DESC ORDER BY timestamp DESC
@ -290,6 +314,8 @@ func (q *Queries) GetUserNotifications(ctx context.Context, arg GetUserNotificat
&i.Priority, &i.Priority,
&i.Version, &i.Version,
&i.Timestamp, &i.Timestamp,
&i.Img,
&i.Expires,
&i.Metadata, &i.Metadata,
); err != nil { ); err != nil {
return nil, err return nil, err
@ -303,7 +329,7 @@ func (q *Queries) GetUserNotifications(ctx context.Context, arg GetUserNotificat
} }
const ListFailedNotifications = `-- name: ListFailedNotifications :many const ListFailedNotifications = `-- name: ListFailedNotifications :many
SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, metadata SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata
FROM notifications FROM notifications
WHERE delivery_status = 'failed' WHERE delivery_status = 'failed'
AND timestamp < NOW() - INTERVAL '1 hour' AND timestamp < NOW() - INTERVAL '1 hour'
@ -334,6 +360,8 @@ func (q *Queries) ListFailedNotifications(ctx context.Context, limit int32) ([]N
&i.Priority, &i.Priority,
&i.Version, &i.Version,
&i.Timestamp, &i.Timestamp,
&i.Img,
&i.Expires,
&i.Metadata, &i.Metadata,
); err != nil { ); err != nil {
return nil, err return nil, err
@ -378,7 +406,7 @@ SET delivery_status = $2,
is_read = $3, is_read = $3,
metadata = $4 metadata = $4
WHERE id = $1 WHERE id = $1
RETURNING id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, metadata RETURNING id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata
` `
type UpdateNotificationStatusParams struct { type UpdateNotificationStatusParams struct {
@ -410,6 +438,8 @@ func (q *Queries) UpdateNotificationStatus(ctx context.Context, arg UpdateNotifi
&i.Priority, &i.Priority,
&i.Version, &i.Version,
&i.Timestamp, &i.Timestamp,
&i.Img,
&i.Expires,
&i.Metadata, &i.Metadata,
) )
return i, err return i, err

View File

@ -11,6 +11,32 @@ import (
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
const DeleteAllCompanyOddsSetting = `-- name: DeleteAllCompanyOddsSetting :exec
DELETE FROM company_odd_settings
WHERE company_id = $1
`
func (q *Queries) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error {
_, err := q.db.Exec(ctx, DeleteAllCompanyOddsSetting, companyID)
return err
}
const DeleteCompanyOddsSettingByOddMarketID = `-- name: DeleteCompanyOddsSettingByOddMarketID :exec
DELETE FROM company_odd_settings
WHERE company_id = $1
AND odds_market_id = $2
`
type DeleteCompanyOddsSettingByOddMarketIDParams struct {
CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
}
func (q *Queries) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, arg DeleteCompanyOddsSettingByOddMarketIDParams) error {
_, err := q.db.Exec(ctx, DeleteCompanyOddsSettingByOddMarketID, arg.CompanyID, arg.OddsMarketID)
return err
}
const DeleteOddsForEvent = `-- name: DeleteOddsForEvent :exec const DeleteOddsForEvent = `-- name: DeleteOddsForEvent :exec
DELETE FROM odds_market DELETE FROM odds_market
Where event_id = $1 Where event_id = $1
@ -22,7 +48,7 @@ func (q *Queries) DeleteOddsForEvent(ctx context.Context, eventID int64) error {
} }
const GetAllOdds = `-- name: GetAllOdds :many const GetAllOdds = `-- name: GetAllOdds :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 SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, number_of_outcomes, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event FROM odds_market_with_event
LIMIT $2 OFFSET $1 LIMIT $2 OFFSET $1
` `
@ -49,6 +75,7 @@ func (q *Queries) GetAllOdds(ctx context.Context, arg GetAllOddsParams) ([]OddsM
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.RawOdds, &i.RawOdds,
&i.NumberOfOutcomes,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
@ -74,6 +101,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,
@ -94,19 +122,20 @@ type GetAllOddsWithSettingsParams struct {
} }
type GetAllOddsWithSettingsRow struct { type GetAllOddsWithSettingsRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` DefaultIsActive bool `json:"default_is_active"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
CompanyID pgtype.Int8 `json:"company_id"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
IsActive bool `json:"is_active"` CompanyID pgtype.Int8 `json:"company_id"`
RawOdds []byte `json:"raw_odds"` IsActive bool `json:"is_active"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWithSettingsParams) ([]GetAllOddsWithSettingsRow, error) { func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWithSettingsParams) ([]GetAllOddsWithSettingsRow, error) {
@ -125,6 +154,7 @@ func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWith
&i.MarketName, &i.MarketName,
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.NumberOfOutcomes,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
@ -144,7 +174,7 @@ func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWith
} }
const GetOddByID = `-- name: GetOddByID :one 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 SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, number_of_outcomes, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event FROM odds_market_with_event
WHERE id = $1 WHERE id = $1
` `
@ -160,6 +190,7 @@ func (q *Queries) GetOddByID(ctx context.Context, id int64) (OddsMarketWithEvent
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.RawOdds, &i.RawOdds,
&i.NumberOfOutcomes,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
@ -172,7 +203,7 @@ func (q *Queries) GetOddByID(ctx context.Context, id int64) (OddsMarketWithEvent
} }
const GetOddsByEventID = `-- name: GetOddsByEventID :many 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 SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, number_of_outcomes, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event FROM odds_market_with_event
WHERE event_id = $1 WHERE event_id = $1
AND ( AND (
@ -223,6 +254,7 @@ func (q *Queries) GetOddsByEventID(ctx context.Context, arg GetOddsByEventIDPara
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.RawOdds, &i.RawOdds,
&i.NumberOfOutcomes,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
@ -242,7 +274,7 @@ func (q *Queries) GetOddsByEventID(ctx context.Context, arg GetOddsByEventIDPara
} }
const GetOddsByMarketID = `-- name: GetOddsByMarketID :one const GetOddsByMarketID = `-- name: GetOddsByMarketID :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 SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, number_of_outcomes, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event FROM odds_market_with_event
WHERE market_id = $1 WHERE market_id = $1
AND event_id = $2 AND event_id = $2
@ -264,6 +296,7 @@ func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDPa
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.RawOdds, &i.RawOdds,
&i.NumberOfOutcomes,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
@ -282,6 +315,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,
@ -304,19 +338,20 @@ type GetOddsWithSettingsByEventIDParams struct {
} }
type GetOddsWithSettingsByEventIDRow struct { type GetOddsWithSettingsByEventIDRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` DefaultIsActive bool `json:"default_is_active"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
CompanyID pgtype.Int8 `json:"company_id"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
IsActive bool `json:"is_active"` CompanyID pgtype.Int8 `json:"company_id"`
RawOdds []byte `json:"raw_odds"` IsActive bool `json:"is_active"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsWithSettingsByEventIDParams) ([]GetOddsWithSettingsByEventIDRow, error) { func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsWithSettingsByEventIDParams) ([]GetOddsWithSettingsByEventIDRow, error) {
@ -340,6 +375,7 @@ func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsW
&i.MarketName, &i.MarketName,
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.NumberOfOutcomes,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
@ -365,6 +401,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,
@ -384,19 +421,20 @@ type GetOddsWithSettingsByIDParams struct {
} }
type GetOddsWithSettingsByIDRow struct { type GetOddsWithSettingsByIDRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` DefaultIsActive bool `json:"default_is_active"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
CompanyID pgtype.Int8 `json:"company_id"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
IsActive bool `json:"is_active"` CompanyID pgtype.Int8 `json:"company_id"`
RawOdds []byte `json:"raw_odds"` IsActive bool `json:"is_active"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSettingsByIDParams) (GetOddsWithSettingsByIDRow, error) { func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSettingsByIDParams) (GetOddsWithSettingsByIDRow, error) {
@ -409,6 +447,7 @@ func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSe
&i.MarketName, &i.MarketName,
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.NumberOfOutcomes,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
@ -427,6 +466,7 @@ SELECT o.id,
o.market_name, o.market_name,
o.market_category, o.market_category,
o.market_id, o.market_id,
o.number_of_outcomes,
o.default_is_active, o.default_is_active,
o.fetched_at, o.fetched_at,
o.expires_at, o.expires_at,
@ -448,19 +488,20 @@ type GetOddsWithSettingsByMarketIDParams struct {
} }
type GetOddsWithSettingsByMarketIDRow struct { type GetOddsWithSettingsByMarketIDRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` DefaultIsActive bool `json:"default_is_active"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
CompanyID pgtype.Int8 `json:"company_id"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
IsActive bool `json:"is_active"` CompanyID pgtype.Int8 `json:"company_id"`
RawOdds []byte `json:"raw_odds"` IsActive bool `json:"is_active"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (GetOddsWithSettingsByMarketIDRow, error) { func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (GetOddsWithSettingsByMarketIDRow, error) {
@ -473,6 +514,7 @@ func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOdds
&i.MarketName, &i.MarketName,
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.NumberOfOutcomes,
&i.DefaultIsActive, &i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
@ -491,6 +533,7 @@ INSERT INTO odds_market (
market_name, market_name,
market_category, market_category,
market_id, market_id,
number_of_outcomes,
raw_odds, raw_odds,
fetched_at, fetched_at,
expires_at expires_at
@ -503,26 +546,29 @@ VALUES (
$5, $5,
$6, $6,
$7, $7,
$8 $8,
$9
) ON CONFLICT (event_id, market_id) DO ) ON CONFLICT (event_id, market_id) DO
UPDATE UPDATE
SET market_type = EXCLUDED.market_type, SET market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name, market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category, market_category = EXCLUDED.market_category,
raw_odds = EXCLUDED.raw_odds, raw_odds = EXCLUDED.raw_odds,
number_of_outcomes = EXCLUDED.number_of_outcomes,
fetched_at = EXCLUDED.fetched_at, fetched_at = EXCLUDED.fetched_at,
expires_at = EXCLUDED.expires_at expires_at = EXCLUDED.expires_at
` `
type InsertOddsMarketParams struct { type InsertOddsMarketParams struct {
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
RawOdds []byte `json:"raw_odds"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` RawOdds []byte `json:"raw_odds"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
} }
func (q *Queries) InsertOddsMarket(ctx context.Context, arg InsertOddsMarketParams) error { func (q *Queries) InsertOddsMarket(ctx context.Context, arg InsertOddsMarketParams) error {
@ -532,6 +578,7 @@ func (q *Queries) InsertOddsMarket(ctx context.Context, arg InsertOddsMarketPara
arg.MarketName, arg.MarketName,
arg.MarketCategory, arg.MarketCategory,
arg.MarketID, arg.MarketID,
arg.NumberOfOutcomes,
arg.RawOdds, arg.RawOdds,
arg.FetchedAt, arg.FetchedAt,
arg.ExpiresAt, arg.ExpiresAt,
@ -568,3 +615,19 @@ func (q *Queries) SaveOddSettings(ctx context.Context, arg SaveOddSettingsParams
) )
return err return err
} }
const UpdateGlobalOddsSetting = `-- name: UpdateGlobalOddsSetting :exec
UPDATE odds_market
SET default_is_active = COALESCE($2, default_is_active)
WHERE id = $1
`
type UpdateGlobalOddsSettingParams struct {
ID int64 `json:"id"`
DefaultIsActive pgtype.Bool `json:"default_is_active"`
}
func (q *Queries) UpdateGlobalOddsSetting(ctx context.Context, arg UpdateGlobalOddsSettingParams) error {
_, err := q.db.Exec(ctx, UpdateGlobalOddsSetting, arg.ID, arg.DefaultIsActive)
return err
}

View File

@ -35,6 +35,19 @@ func (q *Queries) AddSportRaffleFilter(ctx context.Context, arg AddSportRaffleFi
return i, err return i, err
} }
const CheckSportRaffleHasFilter = `-- name: CheckSportRaffleHasFilter :one
SELECT EXISTS (
SELECT 1 FROM raffle_sport_filters WHERE raffle_id = $1
) AS has_filter
`
func (q *Queries) CheckSportRaffleHasFilter(ctx context.Context, raffleID int32) (bool, error) {
row := q.db.QueryRow(ctx, CheckSportRaffleHasFilter, raffleID)
var has_filter bool
err := row.Scan(&has_filter)
return has_filter, err
}
const CheckValidSportRaffleFilter = `-- name: CheckValidSportRaffleFilter :one const CheckValidSportRaffleFilter = `-- name: CheckValidSportRaffleFilter :one
SELECT COUNT(*) > 0 AS exists SELECT COUNT(*) > 0 AS exists
FROM raffle_sport_filters FROM raffle_sport_filters
@ -57,16 +70,17 @@ func (q *Queries) CheckValidSportRaffleFilter(ctx context.Context, arg CheckVali
} }
const CreateRaffle = `-- name: CreateRaffle :one const CreateRaffle = `-- name: CreateRaffle :one
INSERT INTO raffles (company_id, name, expires_at, type) INSERT INTO raffles (company_id, name, expires_at, ticket_limit, type)
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4, $5)
RETURNING id, company_id, name, created_at, expires_at, type, status RETURNING id, company_id, name, created_at, expires_at, ticket_limit, type, status
` `
type CreateRaffleParams struct { type CreateRaffleParams struct {
CompanyID int32 `json:"company_id"` CompanyID int32 `json:"company_id"`
Name string `json:"name"` Name string `json:"name"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
Type string `json:"type"` TicketLimit int32 `json:"ticket_limit"`
Type string `json:"type"`
} }
func (q *Queries) CreateRaffle(ctx context.Context, arg CreateRaffleParams) (Raffle, error) { func (q *Queries) CreateRaffle(ctx context.Context, arg CreateRaffleParams) (Raffle, error) {
@ -74,6 +88,7 @@ func (q *Queries) CreateRaffle(ctx context.Context, arg CreateRaffleParams) (Raf
arg.CompanyID, arg.CompanyID,
arg.Name, arg.Name,
arg.ExpiresAt, arg.ExpiresAt,
arg.TicketLimit,
arg.Type, arg.Type,
) )
var i Raffle var i Raffle
@ -83,6 +98,7 @@ func (q *Queries) CreateRaffle(ctx context.Context, arg CreateRaffleParams) (Raf
&i.Name, &i.Name,
&i.CreatedAt, &i.CreatedAt,
&i.ExpiresAt, &i.ExpiresAt,
&i.TicketLimit,
&i.Type, &i.Type,
&i.Status, &i.Status,
) )
@ -140,7 +156,7 @@ func (q *Queries) CreateRaffleWinner(ctx context.Context, arg CreateRaffleWinner
const DeleteRaffle = `-- name: DeleteRaffle :one const DeleteRaffle = `-- name: DeleteRaffle :one
DELETE FROM raffles DELETE FROM raffles
WHERE id = $1 WHERE id = $1
RETURNING id, company_id, name, created_at, expires_at, type, status RETURNING id, company_id, name, created_at, expires_at, ticket_limit, type, status
` `
func (q *Queries) DeleteRaffle(ctx context.Context, id int32) (Raffle, error) { func (q *Queries) DeleteRaffle(ctx context.Context, id int32) (Raffle, error) {
@ -152,6 +168,7 @@ func (q *Queries) DeleteRaffle(ctx context.Context, id int32) (Raffle, error) {
&i.Name, &i.Name,
&i.CreatedAt, &i.CreatedAt,
&i.ExpiresAt, &i.ExpiresAt,
&i.TicketLimit,
&i.Type, &i.Type,
&i.Status, &i.Status,
) )
@ -219,8 +236,40 @@ func (q *Queries) GetRaffleStanding(ctx context.Context, arg GetRaffleStandingPa
return items, nil return items, nil
} }
const GetRaffleTicketCount = `-- name: GetRaffleTicketCount :one
SELECT COUNT(*)
FROM raffle_tickets
WHERE raffle_id = $1
AND user_id = $2
`
type GetRaffleTicketCountParams struct {
RaffleID int32 `json:"raffle_id"`
UserID int32 `json:"user_id"`
}
func (q *Queries) GetRaffleTicketCount(ctx context.Context, arg GetRaffleTicketCountParams) (int64, error) {
row := q.db.QueryRow(ctx, GetRaffleTicketCount, arg.RaffleID, arg.UserID)
var count int64
err := row.Scan(&count)
return count, err
}
const GetRaffleTicketLimit = `-- name: GetRaffleTicketLimit :one
SELECT ticket_limit
FROM raffles
WHERE id = $1
`
func (q *Queries) GetRaffleTicketLimit(ctx context.Context, id int32) (int32, error) {
row := q.db.QueryRow(ctx, GetRaffleTicketLimit, id)
var ticket_limit int32
err := row.Scan(&ticket_limit)
return ticket_limit, err
}
const GetRafflesOfCompany = `-- name: GetRafflesOfCompany :many const GetRafflesOfCompany = `-- name: GetRafflesOfCompany :many
SELECT id, company_id, name, created_at, expires_at, type, status FROM raffles WHERE company_id = $1 SELECT id, company_id, name, created_at, expires_at, ticket_limit, type, status FROM raffles WHERE company_id = $1
` `
func (q *Queries) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]Raffle, error) { func (q *Queries) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]Raffle, error) {
@ -238,6 +287,7 @@ func (q *Queries) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]R
&i.Name, &i.Name,
&i.CreatedAt, &i.CreatedAt,
&i.ExpiresAt, &i.ExpiresAt,
&i.TicketLimit,
&i.Type, &i.Type,
&i.Status, &i.Status,
); err != nil { ); err != nil {

View File

@ -173,7 +173,7 @@ func (q *Queries) CreateShopTransaction(ctx context.Context, arg CreateShopTrans
} }
const GetAllShopBets = `-- name: GetAllShopBets :many const GetAllShopBets = `-- name: GetAllShopBets :many
SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, outcomes SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes
FROM shop_bet_detail FROM shop_bet_detail
WHERE ( WHERE (
full_name ILIKE '%' || $1 || '%' full_name ILIKE '%' || $1 || '%'
@ -239,6 +239,7 @@ func (q *Queries) GetAllShopBets(ctx context.Context, arg GetAllShopBetsParams)
&i.TransactionVerified, &i.TransactionVerified,
&i.Status, &i.Status,
&i.TotalOdds, &i.TotalOdds,
&i.FastCode,
&i.Outcomes, &i.Outcomes,
); err != nil { ); err != nil {
return nil, err return nil, err
@ -419,7 +420,7 @@ func (q *Queries) GetAllShopTransactions(ctx context.Context, arg GetAllShopTran
} }
const GetShopBetByBetID = `-- name: GetShopBetByBetID :one const GetShopBetByBetID = `-- name: GetShopBetByBetID :one
SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, outcomes SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes
FROM shop_bet_detail FROM shop_bet_detail
WHERE bet_id = $1 WHERE bet_id = $1
` `
@ -445,13 +446,14 @@ func (q *Queries) GetShopBetByBetID(ctx context.Context, betID int64) (ShopBetDe
&i.TransactionVerified, &i.TransactionVerified,
&i.Status, &i.Status,
&i.TotalOdds, &i.TotalOdds,
&i.FastCode,
&i.Outcomes, &i.Outcomes,
) )
return i, err return i, err
} }
const GetShopBetByCashoutID = `-- name: GetShopBetByCashoutID :one const GetShopBetByCashoutID = `-- name: GetShopBetByCashoutID :one
SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, outcomes SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes
FROM shop_bet_detail FROM shop_bet_detail
WHERE cashout_id = $1 WHERE cashout_id = $1
` `
@ -477,13 +479,14 @@ func (q *Queries) GetShopBetByCashoutID(ctx context.Context, cashoutID string) (
&i.TransactionVerified, &i.TransactionVerified,
&i.Status, &i.Status,
&i.TotalOdds, &i.TotalOdds,
&i.FastCode,
&i.Outcomes, &i.Outcomes,
) )
return i, err return i, err
} }
const GetShopBetByID = `-- name: GetShopBetByID :one const GetShopBetByID = `-- name: GetShopBetByID :one
SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, outcomes SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes
FROM shop_bet_detail FROM shop_bet_detail
WHERE id = $1 WHERE id = $1
` `
@ -509,13 +512,14 @@ func (q *Queries) GetShopBetByID(ctx context.Context, id int64) (ShopBetDetail,
&i.TransactionVerified, &i.TransactionVerified,
&i.Status, &i.Status,
&i.TotalOdds, &i.TotalOdds,
&i.FastCode,
&i.Outcomes, &i.Outcomes,
) )
return i, err return i, err
} }
const GetShopBetByShopTransactionID = `-- name: GetShopBetByShopTransactionID :one const GetShopBetByShopTransactionID = `-- name: GetShopBetByShopTransactionID :one
SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, outcomes SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes
FROM shop_bet_detail FROM shop_bet_detail
WHERE shop_transaction_id = $1 WHERE shop_transaction_id = $1
` `
@ -541,6 +545,7 @@ func (q *Queries) GetShopBetByShopTransactionID(ctx context.Context, shopTransac
&i.TransactionVerified, &i.TransactionVerified,
&i.Status, &i.Status,
&i.TotalOdds, &i.TotalOdds,
&i.FastCode,
&i.Outcomes, &i.Outcomes,
) )
return i, err return i, err

8
go.mod
View File

@ -81,19 +81,11 @@ require (
require github.com/twilio/twilio-go v1.26.3 require github.com/twilio/twilio-go v1.26.3
require ( require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/redis/go-redis/v9 v9.10.0 // direct
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
) )
require (
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/segmentio/kafka-go v0.4.48 // direct
)
// require github.com/AnaniyaBelew/ArifpayGoPlugin v0.0.0-20231127130208-54b9bc51118f // require github.com/AnaniyaBelew/ArifpayGoPlugin v0.0.0-20231127130208-54b9bc51118f
// require github.com/AnaniyaBelew/ArifpayGoPlugin v0.0.0-20231127130208-54b9bc51118f // direct // require github.com/AnaniyaBelew/ArifpayGoPlugin v0.0.0-20231127130208-54b9bc51118f // direct

33
go.sum
View File

@ -1,5 +1,3 @@
github.com/AnaniyaBelew/ArifpayGoPlugin v0.0.0-20231127130208-54b9bc51118f h1:UOp9At84RG8OT2Nw2TQidYWaoW1rAfTqChOJLdhYcm8=
github.com/AnaniyaBelew/ArifpayGoPlugin v0.0.0-20231127130208-54b9bc51118f/go.mod h1:N2NQ6ad3i+oLQU+MlPci2f7mx6Mtg+wUcvzCv77KCl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
@ -12,17 +10,11 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
@ -31,8 +23,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -91,7 +81,6 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
@ -129,15 +118,11 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs=
github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/resend/resend-go/v2 v2.20.0 h1:MrIrgV0aHhwRgmcRPw33Nexn6aGJvCvG2XwfFpAMBGM= github.com/resend/resend-go/v2 v2.20.0 h1:MrIrgV0aHhwRgmcRPw33Nexn6aGJvCvG2XwfFpAMBGM=
github.com/resend/resend-go/v2 v2.20.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ= github.com/resend/resend-go/v2 v2.20.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@ -150,8 +135,6 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/segmentio/kafka-go v0.4.48 h1:9jyu9CWK4W5W+SroCe8EffbrRZVqAOkuaLd/ApID4Vs=
github.com/segmentio/kafka-go v0.4.48/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@ -215,12 +198,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -233,15 +214,11 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -257,25 +234,16 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -284,7 +252,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -90,6 +90,7 @@ type GetBet struct {
PhoneNumber string PhoneNumber string
UserID int64 UserID int64
CompanyID int64 CompanyID int64
CompanySlug string
IsShopBet bool IsShopBet bool
CashedOut bool CashedOut bool
Outcomes []BetOutcome Outcomes []BetOutcome
@ -149,18 +150,49 @@ type CreateBetRes struct {
FastCode string `json:"fast_code"` FastCode string `json:"fast_code"`
} }
type BetRes struct { type BetRes struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
Outcomes []BetOutcome `json:"outcomes"` Outcomes []BetOutcome `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"` Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"` TotalOdds float32 `json:"total_odds" example:"4.22"`
Status OutcomeStatus `json:"status" example:"1"` Status OutcomeStatus `json:"status" example:"1"`
Fullname string `json:"full_name" example:"John Smith"` Fullname string `json:"full_name" example:"John Smith"`
UserID int64 `json:"user_id" example:"2"` UserID int64 `json:"user_id" example:"2"`
CompanyID int64 `json:"company_id" example:"1"` CompanyID int64 `json:"company_id" example:"1"`
IsShopBet bool `json:"is_shop_bet" example:"false"` CompanySlug string `json:"company_slug" example:"fortune"`
CashedOut bool `json:"cashed_out" example:"false"` IsShopBet bool `json:"is_shop_bet" example:"false"`
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"` CashedOut bool `json:"cashed_out" example:"false"`
FastCode string `json:"fast_code"` CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
FastCode string `json:"fast_code"`
}
type BetOutcomeViewRes struct {
ID int64 `json:"id"`
BetID int64 `json:"bet_id"`
CompanyName string `json:"company_name"`
SportID int64 `json:"sport_id"`
EventID int64 `json:"event_id"`
OddID int64 `json:"odd_id"`
HomeTeamName string `json:"home_team_name"`
AwayTeamName string `json:"away_team_name"`
MarketID int64 `json:"market_id"`
MarketName string `json:"market_name"`
Odd float32 `json:"odd"`
OddName string `json:"odd_name"`
OddHeader string `json:"odd_header"`
OddHandicap string `json:"odd_handicap"`
Status OutcomeStatus `json:"status"`
Expires time.Time `json:"expires"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
}
type BetOutcomeViewFilter struct {
OutcomeStatus ValidOutcomeStatus
CompanyID ValidInt64
Limit ValidInt32
Offset ValidInt32
} }
func ConvertCreateBetRes(bet Bet, createdNumber int64) CreateBetRes { func ConvertCreateBetRes(bet Bet, createdNumber int64) CreateBetRes {
@ -179,18 +211,19 @@ func ConvertCreateBetRes(bet Bet, createdNumber int64) CreateBetRes {
func ConvertBet(bet GetBet) BetRes { func ConvertBet(bet GetBet) BetRes {
return BetRes{ return BetRes{
ID: bet.ID, ID: bet.ID,
Amount: bet.Amount.Float32(), Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds, TotalOdds: bet.TotalOdds,
Status: bet.Status, Status: bet.Status,
Fullname: bet.FullName, Fullname: bet.FullName,
UserID: bet.UserID, UserID: bet.UserID,
CompanyID: bet.CompanyID, CompanyID: bet.CompanyID,
Outcomes: bet.Outcomes, CompanySlug: bet.CompanySlug,
IsShopBet: bet.IsShopBet, Outcomes: bet.Outcomes,
CashedOut: bet.CashedOut, IsShopBet: bet.IsShopBet,
CreatedAt: bet.CreatedAt, CashedOut: bet.CashedOut,
FastCode: bet.FastCode, CreatedAt: bet.CreatedAt,
FastCode: bet.FastCode,
} }
} }
@ -228,6 +261,30 @@ func ConvertDBBetOutcomes(outcome dbgen.BetOutcome) BetOutcome {
Expires: outcome.Expires.Time, Expires: outcome.Expires.Time,
} }
} }
func ConvertDBBetOutcomesView(outcome dbgen.GetBetOutcomeViewByEventIDRow) BetOutcomeViewRes {
return BetOutcomeViewRes{
ID: outcome.ID,
BetID: outcome.BetID,
CompanyName: outcome.CompanyName,
SportID: outcome.SportID,
EventID: outcome.EventID,
OddID: outcome.OddID,
HomeTeamName: outcome.HomeTeamName,
AwayTeamName: outcome.AwayTeamName,
MarketID: outcome.MarketID,
MarketName: outcome.MarketName,
Odd: outcome.Odd,
OddName: outcome.OddName,
OddHeader: outcome.OddHeader,
OddHandicap: outcome.OddHandicap,
Status: OutcomeStatus(outcome.Status),
Expires: outcome.Expires.Time,
FirstName: outcome.FirstName,
LastName: outcome.LastName,
Amount: outcome.Amount,
TotalOdds: outcome.TotalOdds,
}
}
func ConvertDBBetWithOutcomes(bet dbgen.BetWithOutcome) GetBet { func ConvertDBBetWithOutcomes(bet dbgen.BetWithOutcome) GetBet {
var outcomes []BetOutcome = make([]BetOutcome, 0, len(bet.Outcomes)) var outcomes []BetOutcome = make([]BetOutcome, 0, len(bet.Outcomes))
@ -239,6 +296,7 @@ func ConvertDBBetWithOutcomes(bet dbgen.BetWithOutcome) GetBet {
return GetBet{ return GetBet{
ID: bet.ID, ID: bet.ID,
CompanyID: bet.CompanyID, CompanyID: bet.CompanyID,
CompanySlug: bet.CompanySlug,
Amount: Currency(bet.Amount), Amount: Currency(bet.Amount),
TotalOdds: bet.TotalOdds, TotalOdds: bet.TotalOdds,
Status: OutcomeStatus(bet.Status), Status: OutcomeStatus(bet.Status),

View File

@ -45,6 +45,8 @@ type CreateCompany struct {
AdminID int64 AdminID int64
WalletID int64 WalletID int64
DeductedPercentage float32 DeductedPercentage float32
Slug string
IsActive bool
} }
type UpdateCompany struct { type UpdateCompany struct {
@ -53,18 +55,22 @@ type UpdateCompany struct {
AdminID ValidInt64 AdminID ValidInt64
IsActive ValidBool IsActive ValidBool
DeductedPercentage ValidFloat32 DeductedPercentage ValidFloat32
Slug ValidString
} }
type CreateCompanyReq struct { type CreateCompanyReq struct {
Name string `json:"name" example:"CompanyName"` Name string `json:"name" example:"CompanyName"`
AdminID int64 `json:"admin_id" example:"1"` AdminID int64 `json:"admin_id" example:"1"`
DeductedPercentage float32 `json:"deducted_percentage" example:"0.1" validate:"lt=1"` DeductedPercentage float32 `json:"deducted_percentage" example:"0.1" validate:"lt=1"`
Slug string `json:"slug"`
IsActive bool `json:"is_active"`
} }
type UpdateCompanyReq struct { type UpdateCompanyReq struct {
Name *string `json:"name,omitempty" example:"CompanyName"` Name *string `json:"name,omitempty" example:"CompanyName"`
AdminID *int64 `json:"admin_id,omitempty" example:"1"` AdminID *int64 `json:"admin_id,omitempty" example:"1"`
IsActive *bool `json:"is_active,omitempty" example:"true"` IsActive *bool `json:"is_active,omitempty" example:"true"`
DeductedPercentage *float32 `json:"deducted_percentage,omitempty" example:"0.1" validate:"lt=1"` DeductedPercentage *float32 `json:"deducted_percentage,omitempty" example:"0.1" validate:"lt=1"`
Slug *string `json:"slug"`
} }
type CompanyRes struct { type CompanyRes struct {
@ -113,13 +119,14 @@ func ConvertGetCompany(company GetCompany) GetCompanyRes {
} }
} }
func ConvertCreateCompany(company CreateCompany, uniqueSlug string) dbgen.CreateCompanyParams { func ConvertCreateCompany(company CreateCompany) dbgen.CreateCompanyParams {
return dbgen.CreateCompanyParams{ return dbgen.CreateCompanyParams{
Name: company.Name, Name: company.Name,
Slug: uniqueSlug, Slug: company.Slug,
AdminID: company.AdminID, AdminID: company.AdminID,
WalletID: company.WalletID, WalletID: company.WalletID,
DeductedPercentage: company.DeductedPercentage, DeductedPercentage: company.DeductedPercentage,
IsActive: company.IsActive,
} }
} }
@ -154,31 +161,22 @@ func ConvertDBCompanyDetails(dbCompany dbgen.CompaniesDetail) GetCompany {
func ConvertUpdateCompany(updateCompany UpdateCompany) dbgen.UpdateCompanyParams { func ConvertUpdateCompany(updateCompany UpdateCompany) dbgen.UpdateCompanyParams {
newUpdateCompany := dbgen.UpdateCompanyParams{ newUpdateCompany := dbgen.UpdateCompanyParams{
ID: updateCompany.ID, ID: updateCompany.ID,
Name: pgtype.Text{ Name: updateCompany.Name.ToPG(),
String: updateCompany.Name.Value, AdminID: updateCompany.AdminID.ToPG(),
Valid: updateCompany.Name.Valid, IsActive: updateCompany.IsActive.ToPG(),
}, DeductedPercentage: updateCompany.DeductedPercentage.ToPG(),
AdminID: pgtype.Int8{ Slug: updateCompany.Slug.ToPG(),
Int64: updateCompany.AdminID.Value,
Valid: updateCompany.AdminID.Valid,
},
IsActive: pgtype.Bool{
Bool: updateCompany.IsActive.Value,
Valid: updateCompany.IsActive.Valid,
},
DeductedPercentage: pgtype.Float4{
Float32: updateCompany.DeductedPercentage.Value,
Valid: updateCompany.DeductedPercentage.Valid,
},
} }
return newUpdateCompany return newUpdateCompany
} }
func ConvertUpdateCompanyReq(req UpdateCompanyReq) UpdateCompany { func ConvertUpdateCompanyReq(req UpdateCompanyReq, companyID int64) UpdateCompany {
var updateCompany UpdateCompany var updateCompany UpdateCompany
updateCompany.ID = companyID
if req.Name != nil { if req.Name != nil {
updateCompany.Name = ValidString{ updateCompany.Name = ValidString{
Value: *req.Name, Value: *req.Name,
@ -206,6 +204,12 @@ func ConvertUpdateCompanyReq(req UpdateCompanyReq) UpdateCompany {
Valid: true, Valid: true,
} }
} }
if req.Slug != nil {
updateCompany.Slug = ValidString{
Value: *req.Slug,
Valid: true,
}
}
return updateCompany return updateCompany
} }

View File

@ -25,49 +25,83 @@ func (m Currency) String() string {
return fmt.Sprintf("$%.2f", m.Float32()) return fmt.Sprintf("$%.2f", m.Float32())
} }
// TODO: Change the currency to this format when implementing multi-currency
// type Currency struct {
// Value int64
// Type IntCurrency
// }
// // ToCurrency converts a float32 (like 12.34) into Currency (stored in cents).
// func NewCurrency(f float32, currencyType IntCurrency) Currency {
// cents := math.Round(float64(f) * 100) // avoid float32 precision issues
// return Currency{
// Value: int64(cents),
// Type: currencyType,
// }
// }
// func NewBase(v int64) Currency {
// return Currency{
// Value: v,
// Type: BASE,
// }
// }
// // Float32 converts a Currency back into float32 (like 12.34).
// func (m Currency) Float32() float32 {
// return float32(m.Value) / 100
// }
// // String returns a formatted Currency value for display.
// func (m Currency) String() string {
// return fmt.Sprintf("$%.2f", m.Float32())
// }
type IntCurrency string type IntCurrency string
const ( const (
ETB IntCurrency = "ETB" // Ethiopian Birr BASE IntCurrency = "BASE"
NGN IntCurrency = "NGN" // Nigerian Naira ETB IntCurrency = "ETB" // Ethiopian Birr
ZAR IntCurrency = "ZAR" // South African Rand NGN IntCurrency = "NGN" // Nigerian Naira
EGP IntCurrency = "EGP" // Egyptian Pound ZAR IntCurrency = "ZAR" // South African Rand
KES IntCurrency = "KES" // Kenyan Shilling EGP IntCurrency = "EGP" // Egyptian Pound
UGX IntCurrency = "UGX" // Ugandan Shilling KES IntCurrency = "KES" // Kenyan Shilling
TZS IntCurrency = "TZS" // Tanzanian Shilling UGX IntCurrency = "UGX" // Ugandan Shilling
RWF IntCurrency = "RWF" // Rwandan Franc TZS IntCurrency = "TZS" // Tanzanian Shilling
BIF IntCurrency = "BIF" // Burundian Franc RWF IntCurrency = "RWF" // Rwandan Franc
XOF IntCurrency = "XOF" // West African CFA Franc (BCEAO) BIF IntCurrency = "BIF" // Burundian Franc
XAF IntCurrency = "XAF" // Central African CFA Franc (BEAC) XOF IntCurrency = "XOF" // West African CFA Franc (BCEAO)
GHS IntCurrency = "GHS" // Ghanaian Cedi XAF IntCurrency = "XAF" // Central African CFA Franc (BEAC)
SDG IntCurrency = "SDG" // Sudanese Pound GHS IntCurrency = "GHS" // Ghanaian Cedi
SSP IntCurrency = "SSP" // South Sudanese Pound SDG IntCurrency = "SDG" // Sudanese Pound
DZD IntCurrency = "DZD" // Algerian Dinar SSP IntCurrency = "SSP" // South Sudanese Pound
MAD IntCurrency = "MAD" // Moroccan Dirham DZD IntCurrency = "DZD" // Algerian Dinar
TND IntCurrency = "TND" // Tunisian Dinar MAD IntCurrency = "MAD" // Moroccan Dirham
LYD IntCurrency = "LYD" // Libyan Dinar TND IntCurrency = "TND" // Tunisian Dinar
MZN IntCurrency = "MZN" // Mozambican Metical LYD IntCurrency = "LYD" // Libyan Dinar
AOA IntCurrency = "AOA" // Angolan Kwanza MZN IntCurrency = "MZN" // Mozambican Metical
BWP IntCurrency = "BWP" // Botswana Pula AOA IntCurrency = "AOA" // Angolan Kwanza
ZMW IntCurrency = "ZMW" // Zambian Kwacha BWP IntCurrency = "BWP" // Botswana Pula
MWK IntCurrency = "MWK" // Malawian Kwacha ZMW IntCurrency = "ZMW" // Zambian Kwacha
LSL IntCurrency = "LSL" // Lesotho Loti MWK IntCurrency = "MWK" // Malawian Kwacha
NAD IntCurrency = "NAD" // Namibian Dollar LSL IntCurrency = "LSL" // Lesotho Loti
SZL IntCurrency = "SZL" // Swazi Lilangeni NAD IntCurrency = "NAD" // Namibian Dollar
CVE IntCurrency = "CVE" // Cape Verdean Escudo SZL IntCurrency = "SZL" // Swazi Lilangeni
GMD IntCurrency = "GMD" // Gambian Dalasi CVE IntCurrency = "CVE" // Cape Verdean Escudo
SLL IntCurrency = "SLL" // Sierra Leonean Leone GMD IntCurrency = "GMD" // Gambian Dalasi
LRD IntCurrency = "LRD" // Liberian Dollar SLL IntCurrency = "SLL" // Sierra Leonean Leone
GNF IntCurrency = "GNF" // Guinean Franc LRD IntCurrency = "LRD" // Liberian Dollar
XCD IntCurrency = "XCD" // Eastern Caribbean Dollar (used in Saint Lucia) GNF IntCurrency = "GNF" // Guinean Franc
MRU IntCurrency = "MRU" // Mauritanian Ouguiya XCD IntCurrency = "XCD" // Eastern Caribbean Dollar (used in Saint Lucia)
KMF IntCurrency = "KMF" // Comorian Franc MRU IntCurrency = "MRU" // Mauritanian Ouguiya
DJF IntCurrency = "DJF" // Djiboutian Franc KMF IntCurrency = "KMF" // Comorian Franc
SOS IntCurrency = "SOS" // Somali Shilling DJF IntCurrency = "DJF" // Djiboutian Franc
ERN IntCurrency = "ERN" // Eritrean Nakfa SOS IntCurrency = "SOS" // Somali Shilling
MGA IntCurrency = "MGA" // Malagasy Ariary ERN IntCurrency = "ERN" // Eritrean Nakfa
SCR IntCurrency = "SCR" // Seychellois Rupee MGA IntCurrency = "MGA" // Malagasy Ariary
MUR IntCurrency = "MUR" // Mauritian Rupee SCR IntCurrency = "SCR" // Seychellois Rupee
MUR IntCurrency = "MUR" // Mauritian Rupee
// International currencies (already listed) // International currencies (already listed)
USD IntCurrency = "USD" // US Dollar USD IntCurrency = "USD" // US Dollar

View File

@ -486,25 +486,23 @@ type CreateEnetpulseFixture struct {
// Full domain model // Full domain model
type EnetpulseFixture struct { type EnetpulseFixture struct {
FixtureID string FixtureID string `json:"id"`
Name string Name string `json:"name"`
SportFK string SportFK string `json:"sportFK"`
TournamentFK string TournamentFK string `json:"tournamentFK"`
TournamentTemplateFK string TournamentTemplateFK string `json:"tournament_templateFK"`
TournamentStageFK string TournamentStageFK string `json:"tournament_stageFK"`
TournamentStageName string TournamentStageName string `json:"tournament_stage_name"`
TournamentName string TournamentName string `json:"tournament_name"`
TournamentTemplateName string TournamentTemplateName string `json:"tournament_template_name"`
SportName string SportName string `json:"sport_name"`
Gender string Gender string `json:"gender"`
StartDate time.Time StartDate string `json:"startdate"` // ISO 8601
StatusType string StatusType string `json:"status_type"`
StatusDescFK string StatusDescFK string `json:"status_descFK"`
RoundTypeFK string RoundTypeFK string `json:"round_typeFK"`
UpdatesCount int UpdatesCount string `json:"n"` // convert to int
LastUpdatedAt time.Time LastUpdatedAt string `json:"ut"` // parse to time.Time
CreatedAt time.Time
UpdatedAt time.Time
} }
type CreateEnetpulseResult struct { type CreateEnetpulseResult struct {
@ -604,7 +602,7 @@ type CreateEnetpulseOutcomeType struct {
LastUpdatedAt time.Time `json:"last_updated_at"` LastUpdatedAt time.Time `json:"last_updated_at"`
} }
type EnetpulsePreodds struct { type EnetpulsePreoddsTemp struct {
PreoddsID string `json:"preodds_id"` PreoddsID string `json:"preodds_id"`
EventFK string `json:"event_fk"` EventFK string `json:"event_fk"`
OutcomeTypeFK string `json:"outcome_type_fk"` OutcomeTypeFK string `json:"outcome_type_fk"`
@ -668,3 +666,41 @@ type EnetpulsePreoddsBettingOffer struct {
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }
type EnetpulseFixtureWithPreodds struct {
FixtureID string
FixtureApiID string
FixtureName string
SportFk string
TournamentFk string
TournamentTemplateFk string
TournamentStageFk string
StartDate time.Time
StatusType string
StatusDescFk string
RoundTypeFk string
UpdatesCount int32
LastUpdatedAt time.Time
CreatedAt time.Time
UpdatedAt time.Time
Preodds []EnetpulsePreodds
}
type EnetpulsePreodds struct {
ID int64
PreoddsID string
EventFK int64
OutcomeTypeFK int32
OutcomeScopeFK int32
OutcomeSubtypeFK int32
EventParticipantNumber int32
IParam string
IParam2 string
DParam string
DParam2 string
SParam string
UpdatesCount int32
LastUpdatedAt time.Time
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@ -1,6 +1,7 @@
package domain package domain
import ( import (
"fmt"
"time" "time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
@ -50,6 +51,58 @@ const (
EVENT_SOURCE_ENET EventSource = "enetpulse" EVENT_SOURCE_ENET EventSource = "enetpulse"
) )
// --- EventStatus Validation ---
func (s EventStatus) IsValid() bool {
switch s {
case STATUS_PENDING,
STATUS_IN_PLAY,
STATUS_TO_BE_FIXED,
STATUS_ENDED,
STATUS_POSTPONED,
STATUS_CANCELLED,
STATUS_WALKOVER,
STATUS_INTERRUPTED,
STATUS_ABANDONED,
STATUS_RETIRED,
STATUS_SUSPENDED,
STATUS_DECIDED_BY_FA,
STATUS_REMOVED:
return true
default:
return false
}
}
func ParseEventStatus(val string) (EventStatus, error) {
s := EventStatus(val)
if !s.IsValid() {
return "", fmt.Errorf("invalid EventStatus: %q", val)
}
return s, nil
}
// --- EventSource Validation ---
func (s EventSource) IsValid() bool {
switch s {
case EVENT_SOURCE_BET365,
EVENT_SOURCE_BWIN,
EVENT_SOURCE_BETFAIR,
EVENT_SOURCE_1XBET,
EVENT_SOURCE_ENET:
return true
default:
return false
}
}
func ParseEventSource(val string) (EventSource, error) {
s := EventSource(val)
if !s.IsValid() {
return "", fmt.Errorf("invalid EventSource: %q", val)
}
return s, nil
}
type BaseEvent struct { type BaseEvent struct {
ID int64 ID int64
SourceEventID string SourceEventID string
@ -67,6 +120,7 @@ type BaseEvent struct {
StartTime time.Time StartTime time.Time
Source EventSource Source EventSource
Status EventStatus Status EventStatus
TotalOddOutcomes int64
IsMonitored bool IsMonitored bool
DefaultIsFeatured bool DefaultIsFeatured bool
DefaultIsActive bool DefaultIsActive bool
@ -96,6 +150,7 @@ type BaseEventRes struct {
StartTime time.Time `json:"start_time"` StartTime time.Time `json:"start_time"`
Source EventSource `json:"source"` Source EventSource `json:"source"`
Status EventStatus `json:"status"` Status EventStatus `json:"status"`
TotalOddOutcomes int64 `json:"total_odd_outcomes"`
IsMonitored bool `json:"is_monitored"` IsMonitored bool `json:"is_monitored"`
DefaultIsFeatured bool `json:"default_is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"` DefaultIsActive bool `json:"default_is_active"`
@ -125,10 +180,11 @@ type EventWithSettings struct {
StartTime time.Time StartTime time.Time
Source EventSource Source EventSource
Status EventStatus Status EventStatus
TotalOddOutcomes int64
IsMonitored bool IsMonitored bool
IsFeatured bool IsFeatured bool
IsActive bool IsActive bool
WinningUpperLimit int32 WinningUpperLimit int64
DefaultIsFeatured bool DefaultIsFeatured bool
DefaultIsActive bool DefaultIsActive bool
DefaultWinningUpperLimit int64 DefaultWinningUpperLimit int64
@ -178,10 +234,11 @@ type EventWithSettingsRes struct {
StartTime time.Time `json:"start_time"` StartTime time.Time `json:"start_time"`
Source EventSource `json:"source"` Source EventSource `json:"source"`
Status EventStatus `json:"status"` Status EventStatus `json:"status"`
TotalOddOutcomes int64 `json:"total_odd_outcomes"`
IsMonitored bool `json:"is_monitored"` IsMonitored bool `json:"is_monitored"`
IsFeatured bool `json:"is_featured"` IsFeatured bool `json:"is_featured"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
WinningUpperLimit int32 `json:"winning_upper_limit"` WinningUpperLimit int64 `json:"winning_upper_limit"`
DefaultIsFeatured bool `json:"default_is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"` DefaultIsActive bool `json:"default_is_active"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
@ -204,12 +261,18 @@ type EventSettings struct {
UpdatedAt time.Time UpdatedAt time.Time
} }
type CreateEventSettings struct { type UpdateTenantEventSettings struct {
CompanyID int64 CompanyID int64
EventID int64 EventID int64
IsActive ValidBool IsActive ValidBool
IsFeatured ValidBool IsFeatured ValidBool
WinningUpperLimit ValidInt WinningUpperLimit ValidInt64
}
type UpdateGlobalEventSettings struct {
EventID int64
IsActive ValidBool
IsFeatured ValidBool
WinningUpperLimit ValidInt64
} }
type ValidEventStatus struct { type ValidEventStatus struct {
@ -273,6 +336,7 @@ func ConvertDBEvent(event dbgen.EventWithCountry) BaseEvent {
StartTime: event.StartTime.Time.UTC(), StartTime: event.StartTime.Time.UTC(),
Source: EventSource(event.Source), Source: EventSource(event.Source),
Status: EventStatus(event.Status), Status: EventStatus(event.Status),
TotalOddOutcomes: event.TotalOutcomes,
DefaultIsFeatured: event.DefaultIsFeatured, DefaultIsFeatured: event.DefaultIsFeatured,
IsMonitored: event.IsMonitored, IsMonitored: event.IsMonitored,
DefaultIsActive: event.DefaultIsActive, DefaultIsActive: event.DefaultIsActive,
@ -331,8 +395,8 @@ func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
} }
} }
func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.SaveEventSettingsParams { func ConvertCreateEventSettings(eventSettings UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams {
return dbgen.SaveEventSettingsParams{ return dbgen.SaveTenantEventSettingsParams{
CompanyID: eventSettings.CompanyID, CompanyID: eventSettings.CompanyID,
EventID: eventSettings.EventID, EventID: eventSettings.EventID,
IsActive: eventSettings.IsActive.ToPG(), IsActive: eventSettings.IsActive.ToPG(),
@ -343,17 +407,19 @@ func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.SaveEve
func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings { func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings {
return EventWithSettings{ return EventWithSettings{
ID: event.ID, ID: event.ID,
SportID: event.SportID, SourceEventID: event.SourceEventID,
MatchName: event.MatchName, WinningUpperLimit: event.WinningUpperLimit,
HomeTeam: event.HomeTeam, SportID: event.SportID,
AwayTeam: event.AwayTeam, MatchName: event.MatchName,
HomeTeamID: event.HomeTeamID, HomeTeam: event.HomeTeam,
AwayTeamID: event.AwayTeamID, AwayTeam: event.AwayTeam,
HomeTeamImage: event.HomeKitImage, HomeTeamID: event.HomeTeamID,
AwayTeamImage: event.AwayKitImage, AwayTeamID: event.AwayTeamID,
LeagueID: event.LeagueID, HomeTeamImage: event.HomeKitImage,
LeagueName: event.LeagueName, AwayTeamImage: event.AwayKitImage,
LeagueID: event.LeagueID,
LeagueName: event.LeagueName,
LeagueCC: ValidString{ LeagueCC: ValidString{
Value: event.LeagueCc.String, Value: event.LeagueCc.String,
Valid: event.LeagueCc.Valid, Valid: event.LeagueCc.Valid,
@ -361,6 +427,7 @@ func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings {
StartTime: event.StartTime.Time.UTC(), StartTime: event.StartTime.Time.UTC(),
Source: EventSource(event.Source), Source: EventSource(event.Source),
Status: EventStatus(event.Status), Status: EventStatus(event.Status),
TotalOddOutcomes: event.TotalOutcomes,
IsFeatured: event.IsFeatured, IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored, IsMonitored: event.IsMonitored,
IsActive: event.IsActive, IsActive: event.IsActive,
@ -401,8 +468,8 @@ func ConvertDBEventWithSettings(events []dbgen.EventWithSetting) []EventWithSett
return result return result
} }
func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.SaveEventSettingsParams { func ConvertUpdateTenantEventSettings(event UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams {
return dbgen.SaveEventSettingsParams{ return dbgen.SaveTenantEventSettingsParams{
EventID: event.EventID, EventID: event.EventID,
CompanyID: event.CompanyID, CompanyID: event.CompanyID,
IsActive: event.IsActive.ToPG(), IsActive: event.IsActive.ToPG(),
@ -410,10 +477,19 @@ func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.SaveEventSettin
WinningUpperLimit: event.WinningUpperLimit.ToPG(), WinningUpperLimit: event.WinningUpperLimit.ToPG(),
} }
} }
func ConvertUpdateGlobalEventSettings(event UpdateGlobalEventSettings) dbgen.UpdateGlobalEventSettingsParams {
return dbgen.UpdateGlobalEventSettingsParams{
ID: event.EventID,
DefaultIsActive: event.IsActive.ToPG(),
DefaultIsFeatured: event.IsFeatured.ToPG(),
DefaultWinningUpperLimit: event.WinningUpperLimit.ToPG(),
}
}
func ConvertEventRes(event BaseEvent) BaseEventRes { func ConvertEventRes(event BaseEvent) BaseEventRes {
return BaseEventRes{ return BaseEventRes{
ID: event.ID, ID: event.ID,
SourceEventID: event.SourceEventID,
SportID: event.SportID, SportID: event.SportID,
MatchName: event.MatchName, MatchName: event.MatchName,
HomeTeam: event.HomeTeam, HomeTeam: event.HomeTeam,
@ -428,6 +504,7 @@ func ConvertEventRes(event BaseEvent) BaseEventRes {
StartTime: event.StartTime.UTC(), StartTime: event.StartTime.UTC(),
Source: EventSource(event.Source), Source: EventSource(event.Source),
Status: EventStatus(event.Status), Status: EventStatus(event.Status),
TotalOddOutcomes: event.TotalOddOutcomes,
DefaultIsFeatured: event.DefaultIsFeatured, DefaultIsFeatured: event.DefaultIsFeatured,
IsMonitored: event.IsMonitored, IsMonitored: event.IsMonitored,
DefaultIsActive: event.DefaultIsActive, DefaultIsActive: event.DefaultIsActive,
@ -452,6 +529,7 @@ func ConvertEventResList(events []BaseEvent) []BaseEventRes {
func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes { func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
return EventWithSettingsRes{ return EventWithSettingsRes{
ID: event.ID, ID: event.ID,
SourceEventID: event.SourceEventID,
SportID: event.SportID, SportID: event.SportID,
MatchName: event.MatchName, MatchName: event.MatchName,
HomeTeam: event.HomeTeam, HomeTeam: event.HomeTeam,
@ -466,6 +544,7 @@ func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
StartTime: event.StartTime.UTC(), StartTime: event.StartTime.UTC(),
Source: EventSource(event.Source), Source: EventSource(event.Source),
Status: EventStatus(event.Status), Status: EventStatus(event.Status),
TotalOddOutcomes: event.TotalOddOutcomes,
IsFeatured: event.IsFeatured, IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored, IsMonitored: event.IsMonitored,
IsActive: event.IsActive, IsActive: event.IsActive,
@ -480,6 +559,7 @@ func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
MatchPeriod: event.MatchPeriod.Value, MatchPeriod: event.MatchPeriod.Value,
IsLive: event.IsLive, IsLive: event.IsLive,
FetchedAt: event.FetchedAt.UTC(), FetchedAt: event.FetchedAt.UTC(),
UpdatedAt: event.UpdatedAt,
} }
} }

View File

@ -87,6 +87,17 @@ type UpdateLeague struct {
SportID ValidInt32 `json:"sport_id" example:"1"` SportID ValidInt32 `json:"sport_id" example:"1"`
} }
type UpdateLeagueSettingsReq struct {
IsFeatured *bool `json:"is_featured" example:"true"`
IsActive *bool `json:"is_active" example:"true"`
}
type UpdateGlobalLeagueSettings struct {
ID int64
DefaultIsActive ValidBool
DefaultIsFeatured ValidBool
}
type LeagueFilter struct { type LeagueFilter struct {
Query ValidString Query ValidString
CountryCode ValidString CountryCode ValidString
@ -109,8 +120,8 @@ func ConvertCreateLeague(league CreateLeague) dbgen.InsertLeagueParams {
} }
} }
func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.InsertLeagueSettingsParams { func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.SaveLeagueSettingsParams {
return dbgen.InsertLeagueSettingsParams{ return dbgen.SaveLeagueSettingsParams{
CompanyID: leagueSetting.CompanyID, CompanyID: leagueSetting.CompanyID,
LeagueID: leagueSetting.LeagueID, LeagueID: leagueSetting.LeagueID,
IsActive: leagueSetting.IsActive.ToPG(), IsActive: leagueSetting.IsActive.ToPG(),
@ -149,7 +160,7 @@ func ConvertDBLeagueWithSetting(lws dbgen.GetAllLeaguesWithSettingsRow) LeagueWi
ID: lws.ID, ID: lws.ID,
Name: lws.Name, Name: lws.Name,
CompanyID: lws.CompanyID.Int64, CompanyID: lws.CompanyID.Int64,
CountryCode: ValidString{ CountryCode: ValidString{
Value: lws.CountryCode.String, Value: lws.CountryCode.String,
Valid: lws.CountryCode.Valid, Valid: lws.CountryCode.Valid,
}, },
@ -187,15 +198,15 @@ func ConvertUpdateLeague(updateLeague UpdateLeague) dbgen.UpdateLeagueParams {
func ConvertLeagueWithSettingRes(lws LeagueWithSettings) LeagueWithSettingsRes { func ConvertLeagueWithSettingRes(lws LeagueWithSettings) LeagueWithSettingsRes {
return LeagueWithSettingsRes{ return LeagueWithSettingsRes{
ID: lws.ID, ID: lws.ID,
Name: lws.Name, Name: lws.Name,
CompanyID: lws.CompanyID, CompanyID: lws.CompanyID,
CountryCode: lws.CountryCode.Value, CountryCode: lws.CountryCode.Value,
Bet365ID: lws.Bet365ID.Value, Bet365ID: lws.Bet365ID.Value,
IsActive: lws.IsActive, IsActive: lws.IsActive,
SportID: lws.SportID, SportID: lws.SportID,
IsFeatured: lws.IsFeatured, IsFeatured: lws.IsFeatured,
UpdatedAt: lws.UpdatedAt, UpdatedAt: lws.UpdatedAt,
DefaultIsActive: lws.DefaultIsActive, DefaultIsActive: lws.DefaultIsActive,
DefaultIsFeatured: lws.DefaultIsFeatured, DefaultIsFeatured: lws.DefaultIsFeatured,
} }
@ -213,12 +224,12 @@ func ConvertLeagueWithSettingResList(leagues []LeagueWithSettings) []LeagueWithS
func ConvertBaseLeagueRes(league BaseLeague) BaseLeagueRes { func ConvertBaseLeagueRes(league BaseLeague) BaseLeagueRes {
return BaseLeagueRes{ return BaseLeagueRes{
ID: league.ID, ID: league.ID,
Name: league.Name, Name: league.Name,
CountryCode: league.CountryCode.Value, CountryCode: league.CountryCode.Value,
Bet365ID: league.Bet365ID.Value, Bet365ID: league.Bet365ID.Value,
SportID: league.SportID, SportID: league.SportID,
DefaultIsActive: league.DefaultIsActive, DefaultIsActive: league.DefaultIsActive,
DefaultIsFeatured: league.DefaultIsFeatured, DefaultIsFeatured: league.DefaultIsFeatured,
} }
} }
@ -231,3 +242,11 @@ func ConvertBaseLeagueResList(leagues []BaseLeague) []BaseLeagueRes {
return result return result
} }
func ConvertUpdateGlobalLeagueSetting(league UpdateGlobalLeagueSettings) dbgen.UpdateGlobalLeagueSettingsParams {
return dbgen.UpdateGlobalLeagueSettingsParams{
ID: league.ID,
IsActive: league.DefaultIsActive.ToPG(),
IsFeatured: league.DefaultIsFeatured.ToPG(),
}
}

View File

@ -84,6 +84,8 @@ type Notification struct {
Priority int `json:"priority,omitempty"` Priority int `json:"priority,omitempty"`
Version int `json:"-"` Version int `json:"-"`
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
Expires time.Time `json:"expires"`
Image string `json:"image"`
Metadata json.RawMessage `json:"metadata,omitempty"` Metadata json.RawMessage `json:"metadata,omitempty"`
} }
type CreateNotification struct { type CreateNotification struct {
@ -97,6 +99,8 @@ type CreateNotification struct {
DeliveryChannel DeliveryChannel `json:"delivery_channel,omitempty"` DeliveryChannel DeliveryChannel `json:"delivery_channel,omitempty"`
Payload NotificationPayload `json:"payload"` Payload NotificationPayload `json:"payload"`
Priority int `json:"priority,omitempty"` Priority int `json:"priority,omitempty"`
Expires time.Time `json:"expires"`
Image string `json:"image,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty"` Metadata json.RawMessage `json:"metadata,omitempty"`
} }

View File

@ -9,42 +9,45 @@ import (
) )
type CreateOddMarket struct { type CreateOddMarket struct {
EventID int64 EventID int64
MarketCategory string MarketCategory string
MarketType string MarketType string
MarketName string MarketName string
MarketID int64 MarketID int64
UpdatedAt time.Time NumberOfOutcomes int64
Odds []map[string]interface{} UpdatedAt time.Time
Odds []map[string]interface{}
} }
type OddMarket struct { type OddMarket struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
RawOdds []json.RawMessage `json:"raw_odds"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt time.Time `json:"fetched_at"` RawOdds []json.RawMessage `json:"raw_odds"`
ExpiresAt time.Time `json:"expires_at"` FetchedAt time.Time `json:"fetched_at"`
DefaultIsActive bool `json:"is_active"` ExpiresAt time.Time `json:"expires_at"`
IsMonitored bool `json:"is_monitored"` DefaultIsActive bool `json:"is_active"`
IsLive bool `json:"is_live"` IsMonitored bool `json:"is_monitored"`
Status EventStatus `json:"status"` IsLive bool `json:"is_live"`
Source EventSource `json:"source"` Status EventStatus `json:"status"`
Source EventSource `json:"source"`
} }
type OddMarketWithSettings struct { type OddMarketWithSettings struct {
ID int64 `json:"id"` ID int64 `json:"id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"` MarketID int64 `json:"market_id"`
RawOdds []json.RawMessage `json:"raw_odds"` NumberOfOutcomes int64 `json:"number_of_outcomes"`
FetchedAt time.Time `json:"fetched_at"` RawOdds []json.RawMessage `json:"raw_odds"`
ExpiresAt time.Time `json:"expires_at"` FetchedAt time.Time `json:"fetched_at"`
IsActive bool `json:"is_active"` ExpiresAt time.Time `json:"expires_at"`
IsActive bool `json:"is_active"`
} }
type OddMarketSettings struct { type OddMarketSettings struct {
@ -61,6 +64,11 @@ type CreateOddMarketSettings struct {
CustomRawOdds []map[string]interface{} CustomRawOdds []map[string]interface{}
} }
type UpdateGlobalOddMarketSettings struct {
OddMarketID int64
IsActive ValidBool
}
type CustomOdd struct { type CustomOdd struct {
OddID int64 `json:"odd_id"` OddID int64 `json:"odd_id"`
OddValue float32 `json:"odd_value"` OddValue float32 `json:"odd_value"`
@ -72,6 +80,11 @@ type CreateOddMarketSettingsReq struct {
CustomOdd []CustomOdd `json:"custom_odd,omitempty"` CustomOdd []CustomOdd `json:"custom_odd,omitempty"`
} }
type UpdateGlobalOddMarketSettingsReq struct {
OddMarketID int64 `json:"odd_market_id"`
IsActive *bool `json:"is_active,omitempty"`
}
type RawOddsByMarketID struct { type RawOddsByMarketID struct {
ID int64 `json:"id"` ID int64 `json:"id"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
@ -86,6 +99,8 @@ type OddMarketFilter struct {
Offset ValidInt32 Offset ValidInt32
} }
type OddMarketWithEventFilter struct { type OddMarketWithEventFilter struct {
Status ValidString
IsLive ValidBool
Limit ValidInt32 Limit ValidInt32
Offset ValidInt32 Offset ValidInt32
} }
@ -100,20 +115,21 @@ func ConvertDBOddMarket(oddMarket dbgen.OddsMarketWithEvent) (OddMarket, error)
rawOdds = []json.RawMessage{} // explicit empty slice rawOdds = []json.RawMessage{} // explicit empty slice
} }
return OddMarket{ return OddMarket{
ID: oddMarket.ID, ID: oddMarket.ID,
EventID: oddMarket.EventID, EventID: oddMarket.EventID,
MarketType: oddMarket.MarketType, MarketType: oddMarket.MarketType,
MarketName: oddMarket.MarketName, MarketName: oddMarket.MarketName,
MarketCategory: oddMarket.MarketCategory, MarketCategory: oddMarket.MarketCategory,
MarketID: oddMarket.MarketID, MarketID: oddMarket.MarketID,
RawOdds: rawOdds, NumberOfOutcomes: oddMarket.NumberOfOutcomes,
FetchedAt: oddMarket.FetchedAt.Time, RawOdds: rawOdds,
ExpiresAt: oddMarket.ExpiresAt.Time, FetchedAt: oddMarket.FetchedAt.Time,
DefaultIsActive: oddMarket.DefaultIsActive, ExpiresAt: oddMarket.ExpiresAt.Time,
IsMonitored: oddMarket.IsMonitored, DefaultIsActive: oddMarket.DefaultIsActive,
IsLive: oddMarket.IsLive, IsMonitored: oddMarket.IsMonitored,
Status: EventStatus(oddMarket.Status), IsLive: oddMarket.IsLive,
Source: EventSource(oddMarket.Source), Status: EventStatus(oddMarket.Status),
Source: EventSource(oddMarket.Source),
}, nil }, nil
} }
@ -136,14 +152,15 @@ func ConvertCreateOddMarket(oddMarket CreateOddMarket) (dbgen.InsertOddsMarketPa
} }
return dbgen.InsertOddsMarketParams{ return dbgen.InsertOddsMarketParams{
EventID: oddMarket.EventID, EventID: oddMarket.EventID,
MarketType: oddMarket.MarketType, MarketType: oddMarket.MarketType,
MarketName: oddMarket.MarketName, MarketName: oddMarket.MarketName,
MarketCategory: oddMarket.MarketCategory, MarketCategory: oddMarket.MarketCategory,
MarketID: oddMarket.MarketID, MarketID: oddMarket.MarketID,
RawOdds: rawOddsBytes, NumberOfOutcomes: oddMarket.NumberOfOutcomes,
FetchedAt: pgtype.Timestamp{Time: time.Now(), Valid: true}, RawOdds: rawOddsBytes,
ExpiresAt: pgtype.Timestamp{Time: (time.Now()).Add(time.Hour), Valid: true}, FetchedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
ExpiresAt: pgtype.Timestamp{Time: (time.Now()).Add(time.Hour), Valid: true},
}, nil }, nil
} }
@ -170,16 +187,17 @@ func ConvertDBOddMarketWithSetting(oms dbgen.OddsMarketWithSetting) (OddMarketWi
rawOdds = []json.RawMessage{} // explicit empty slice rawOdds = []json.RawMessage{} // explicit empty slice
} }
return OddMarketWithSettings{ return OddMarketWithSettings{
ID: oms.ID, ID: oms.ID,
EventID: oms.EventID, EventID: oms.EventID,
MarketType: oms.MarketType, MarketType: oms.MarketType,
MarketName: oms.MarketName, MarketName: oms.MarketName,
MarketCategory: oms.MarketCategory, MarketCategory: oms.MarketCategory,
MarketID: oms.MarketID, MarketID: oms.MarketID,
RawOdds: rawOdds, NumberOfOutcomes: oms.NumberOfOutcomes,
FetchedAt: oms.FetchedAt.Time, RawOdds: rawOdds,
ExpiresAt: oms.ExpiresAt.Time, FetchedAt: oms.FetchedAt.Time,
IsActive: oms.IsActive, ExpiresAt: oms.ExpiresAt.Time,
IsActive: oms.IsActive,
}, nil }, nil
} }

View File

@ -3,13 +3,14 @@ package domain
import "time" import "time"
type Raffle struct { type Raffle struct {
ID int32 ID int32
CompanyID int32 CompanyID int32
Name string Name string
CreatedAt time.Time CreatedAt time.Time
ExpiresAt time.Time ExpiresAt time.Time
Type string TicketLimit int32
Status string Type string
Status string
} }
type RaffleFilter struct { type RaffleFilter struct {
@ -64,10 +65,11 @@ type RaffleTicketRes struct {
} }
type CreateRaffle struct { type CreateRaffle struct {
CompanyID int32 `json:"company_id" validate:"required"` CompanyID int32 `json:"company_id" validate:"required"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
ExpiresAt *time.Time `json:"expires_at" validate:"required"` ExpiresAt *time.Time `json:"expires_at" validate:"required"`
Type string `json:"type" validate:"required"` TicketLimit int32 `json:"ticket_limit" validate:"required"`
Type string `json:"type" validate:"required"`
} }
type CreateRaffleTicket struct { type CreateRaffleTicket struct {

View File

@ -1,6 +1,7 @@
package domain package domain
import ( import (
"fmt"
"time" "time"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
@ -48,6 +49,28 @@ const (
OUTCOME_STATUS_ERROR OutcomeStatus = 5 //Half Win and Half Given Back OUTCOME_STATUS_ERROR OutcomeStatus = 5 //Half Win and Half Given Back
) )
func (o OutcomeStatus) IsValid() bool {
switch o {
case OUTCOME_STATUS_PENDING,
OUTCOME_STATUS_WIN,
OUTCOME_STATUS_LOSS,
OUTCOME_STATUS_VOID,
OUTCOME_STATUS_HALF,
OUTCOME_STATUS_ERROR:
return true
default:
return false
}
}
func ParseOutcomeStatus(val int) (OutcomeStatus, error) {
o := OutcomeStatus(val)
if !o.IsValid() {
return 0, fmt.Errorf("invalid OutcomeStatus: %d", val)
}
return o, nil
}
func (o *OutcomeStatus) String() string { func (o *OutcomeStatus) String() string {
switch *o { switch *o {
case OUTCOME_STATUS_PENDING: case OUTCOME_STATUS_PENDING:
@ -72,7 +95,6 @@ type ValidOutcomeStatus struct {
Valid bool Valid bool
} }
func (v ValidOutcomeStatus) ToPG() pgtype.Int4 { func (v ValidOutcomeStatus) ToPG() pgtype.Int4 {
return pgtype.Int4{ return pgtype.Int4{
Int32: int32(v.Value), Int32: int32(v.Value),
@ -80,7 +102,6 @@ func (v ValidOutcomeStatus) ToPG() pgtype.Int4 {
} }
} }
type TimeStatus int32 type TimeStatus int32
const ( const (

View File

@ -34,6 +34,7 @@ type ShopBetDetail struct {
CompanyID int64 CompanyID int64
FullName string FullName string
PhoneNumber string PhoneNumber string
FastCode string
CashoutID string CashoutID string
CashedOut bool CashedOut bool
BetID int64 BetID int64
@ -80,12 +81,13 @@ type ShopBetRes struct {
CompanyID int64 `json:"company_id" example:"2"` CompanyID int64 `json:"company_id" example:"2"`
FullName string `json:"full_name" example:"John"` FullName string `json:"full_name" example:"John"`
PhoneNumber string `json:"phone_number" example:"1234567890"` PhoneNumber string `json:"phone_number" example:"1234567890"`
FastCode string `json:"fast_code" example:"12SD1"`
CashoutID string `json:"cashout_id" example:"21234"` CashoutID string `json:"cashout_id" example:"21234"`
CashedOut bool `json:"cashed_out" example:"false"` CashedOut bool `json:"cashed_out" example:"false"`
BetID int64 `json:"bet_id" example:"1"` BetID int64 `json:"bet_id" example:"1"`
NumberOfOutcomes int64 `json:"number_of_outcomes" example:"1"` NumberOfOutcomes int64 `json:"number_of_outcomes" example:"1"`
Status OutcomeStatus `json:"status" example:"1"` Status OutcomeStatus `json:"status" example:"1"`
Amount Currency `json:"amount"` Amount float32 `json:"amount"`
Outcomes []BetOutcome `json:"outcomes"` Outcomes []BetOutcome `json:"outcomes"`
TransactionVerified bool `json:"transaction_verified" example:"true"` TransactionVerified bool `json:"transaction_verified" example:"true"`
UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:00:00Z"` UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:00:00Z"`
@ -111,12 +113,13 @@ func ConvertShopBetDetail(shopBet ShopBetDetail) ShopBetRes {
CompanyID: shopBet.CompanyID, CompanyID: shopBet.CompanyID,
FullName: shopBet.FullName, FullName: shopBet.FullName,
PhoneNumber: shopBet.PhoneNumber, PhoneNumber: shopBet.PhoneNumber,
FastCode: shopBet.FastCode,
CashoutID: shopBet.CashoutID, CashoutID: shopBet.CashoutID,
CashedOut: shopBet.CashedOut, CashedOut: shopBet.CashedOut,
BetID: shopBet.BetID, BetID: shopBet.BetID,
NumberOfOutcomes: shopBet.NumberOfOutcomes, NumberOfOutcomes: shopBet.NumberOfOutcomes,
Status: shopBet.Status, Status: shopBet.Status,
Amount: shopBet.Amount, Amount: shopBet.Amount.Float32(),
Outcomes: shopBet.Outcomes, Outcomes: shopBet.Outcomes,
TransactionVerified: shopBet.TransactionVerified, TransactionVerified: shopBet.TransactionVerified,
UpdatedAt: shopBet.UpdatedAt, UpdatedAt: shopBet.UpdatedAt,

View File

@ -1,6 +1,13 @@
package domain package domain
import "time" import (
"errors"
"time"
)
var (
ErrWalletIDDuplicate = errors.New("there already exists user id with wallet_type")
)
type Wallet struct { type Wallet struct {
ID int64 ID int64

View File

@ -103,8 +103,11 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma
Query: filter.Query.ToPG(), Query: filter.Query.ToPG(),
CreatedBefore: filter.CreatedBefore.ToPG(), CreatedBefore: filter.CreatedBefore.ToPG(),
CreatedAfter: filter.CreatedAfter.ToPG(), CreatedAfter: filter.CreatedAfter.ToPG(),
Offset: filter.Offset.ToPG(), Offset: pgtype.Int4{
Limit: filter.Limit.ToPG(), Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
Limit: filter.Limit.ToPG(),
}) })
if err != nil { if err != nil {
domain.MongoDBLogger.Error("failed to get all bets", domain.MongoDBLogger.Error("failed to get all bets",
@ -123,7 +126,7 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma
Query: filter.Query.ToPG(), Query: filter.Query.ToPG(),
CreatedBefore: filter.CreatedBefore.ToPG(), CreatedBefore: filter.CreatedBefore.ToPG(),
CreatedAfter: filter.CreatedAfter.ToPG(), CreatedAfter: filter.CreatedAfter.ToPG(),
}); })
var result []domain.GetBet = make([]domain.GetBet, 0, len(bets)) var result []domain.GetBet = make([]domain.GetBet, 0, len(bets))
for _, bet := range bets { for _, bet := range bets {
@ -275,6 +278,36 @@ func (s *Store) SettleWinningBet(ctx context.Context, betID int64, userID int64,
return nil return nil
} }
func (s *Store) GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error) {
outcomes, err := s.queries.GetBetOutcomeViewByEventID(ctx, dbgen.GetBetOutcomeViewByEventIDParams{
EventID: eventID,
FilterStatus: filter.OutcomeStatus.ToPG(),
CompanyID: filter.CompanyID.ToPG(),
Offset: filter.Offset.ToPG(),
Limit: filter.Limit.ToPG(),
})
if err != nil {
domain.MongoDBLogger.Error("failed to get bet outcomes by event ID",
zap.Int64("event_id", eventID),
zap.Error(err),
)
return nil, 0, err
}
total, err := s.queries.TotalBetOutcomeViewByEventID(ctx, dbgen.TotalBetOutcomeViewByEventIDParams{
EventID: eventID,
FilterStatus: filter.OutcomeStatus.ToPG(),
CompanyID: filter.CompanyID.ToPG(),
})
var result []domain.BetOutcomeViewRes = make([]domain.BetOutcomeViewRes, 0, len(outcomes))
for _, outcome := range outcomes {
result = append(result, domain.ConvertDBBetOutcomesView(outcome))
}
return result, total, nil
}
func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) { func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) {
outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, dbgen.GetBetOutcomeByEventIDParams{ outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, dbgen.GetBetOutcomeByEventIDParams{
@ -377,6 +410,45 @@ func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int6
} }
return result, nil return result, nil
} }
func (s *Store) UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) {
outcomes, err := s.queries.UpdateBetOutcomeStatusForOddID(ctx, dbgen.UpdateBetOutcomeStatusForOddIDParams{
OddID: oddID,
Status: int32(status),
})
if err != nil {
domain.MongoDBLogger.Error("failed to update bet outcome status for oddID",
zap.Int64("oddId", oddID),
zap.Int32("status", int32(status)),
zap.Error(err),
)
return nil, err
}
var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes))
for _, outcome := range outcomes {
result = append(result, domain.ConvertDBBetOutcomes(outcome))
}
return result, nil
}
func (s *Store) BulkUpdateBetOutcomeStatusForOddIds(ctx context.Context, oddID []int64, status domain.OutcomeStatus) (error) {
err := s.queries.BulkUpdateBetOutcomeStatusByOddIDs(ctx, dbgen.BulkUpdateBetOutcomeStatusByOddIDsParams{
Status: int32(status),
OddIds: oddID,
})
if err != nil {
domain.MongoDBLogger.Error("failed to update bet outcome status for oddIDs",
zap.Int64s("oddIds", oddID),
zap.Int32("status", int32(status)),
zap.Error(err),
)
return err
}
return nil
}
func (s *Store) UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error { func (s *Store) UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error {
err := s.queries.UpdateBetWithCashback(ctx, dbgen.UpdateBetWithCashbackParams{ err := s.queries.UpdateBetWithCashback(ctx, dbgen.UpdateBetWithCashbackParams{

View File

@ -68,8 +68,11 @@ func (s *Store) GetAllBranches(ctx context.Context, filter domain.BranchFilter)
return branches, nil return branches, nil
} }
func (s *Store) SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error) { func (s *Store) SearchBranchByName(ctx context.Context, name string, companyID domain.ValidInt64) ([]domain.BranchDetail, error) {
dbBranches, err := s.queries.SearchBranchByName(ctx, pgtype.Text{String: name, Valid: true}) dbBranches, err := s.queries.SearchBranchByName(ctx, dbgen.SearchBranchByNameParams{
Column1: pgtype.Text{String: name, Valid: true},
CompanyID: companyID.ToPG(),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -0,0 +1,12 @@
package repository
import (
"errors"
"github.com/jackc/pgx/v5/pgconn"
)
func IsUniqueViolation(err error) bool {
var pgErr *pgconn.PgError
return errors.As(err, &pgErr) && pgErr.Code == "23505"
}

View File

@ -2,36 +2,34 @@ package repository
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "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" "github.com/jackc/pgx/v5/pgtype"
) )
func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) { func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) {
baseSlug := helpers.GenerateSlug(company.Name) // baseSlug := helpers.GenerateSlug(company.Name)
uniqueSlug := baseSlug // uniqueSlug := baseSlug
i := 1 // i := 1
for { // for {
_, err := s.queries.GetCompanyIDUsingSlug(ctx, uniqueSlug) // _, err := s.queries.GetCompanyUsingSlug(ctx, uniqueSlug)
if err != nil { // if err != nil {
if errors.Is(err, pgx.ErrNoRows) { // if errors.Is(err, pgx.ErrNoRows) {
// slug is unique // // slug is unique
break // break
} else { // } else {
// real DB error // // real DB error
return domain.Company{}, err // return domain.Company{}, err
} // }
} // }
uniqueSlug = fmt.Sprintf("%s-%d", baseSlug, i) // uniqueSlug = fmt.Sprintf("%s-%d", baseSlug, i)
i++ // i++
} // }
fmt.Printf("\ncompany %v\n\n", company)
dbCompany, err := s.queries.CreateCompany(ctx, domain.ConvertCreateCompany(company))
dbCompany, err := s.queries.CreateCompany(ctx, domain.ConvertCreateCompany(company, uniqueSlug))
if err != nil { if err != nil {
return domain.Company{}, err return domain.Company{}, err
} }
@ -78,25 +76,26 @@ func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany
return domain.ConvertDBCompanyDetails(dbCompany), nil return domain.ConvertDBCompanyDetails(dbCompany), nil
} }
func (s *Store) GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error) { func (s *Store) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) {
dbCompanyID, err := s.queries.GetCompanyIDUsingSlug(ctx, slug) dbCompany, err := s.queries.GetCompanyUsingSlug(ctx, slug)
if err != nil {
return 0, err
}
return dbCompanyID, nil
}
func (s *Store) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) {
dbCompany, err := s.queries.UpdateCompany(ctx, domain.ConvertUpdateCompany(company))
if err != nil { if err != nil {
return domain.Company{}, err return domain.Company{}, err
} }
return domain.ConvertDBCompany(dbCompany), nil return domain.ConvertDBCompany(dbCompany), nil
} }
func (s *Store) UpdateCompany(ctx context.Context, company domain.UpdateCompany) error {
fmt.Printf("company %v\n", company)
err := s.queries.UpdateCompany(ctx, domain.ConvertUpdateCompany(company))
if err != nil {
return err
}
return nil
}
func (s *Store) DeleteCompany(ctx context.Context, id int64) error { func (s *Store) DeleteCompany(ctx context.Context, id int64) error {
return s.queries.DeleteCompany(ctx, id) return s.queries.DeleteCompany(ctx, id)
} }

View File

@ -302,6 +302,71 @@ func (s *Store) GetAllEnetpulsePreoddsBettingOffers(ctx context.Context) ([]doma
return offers, nil return offers, nil
} }
func (s *Store) GetFixturesWithPreodds(ctx context.Context) ([]domain.EnetpulseFixtureWithPreodds, error) {
dbRows, err := s.queries.GetFixturesWithPreodds(ctx)
if err != nil {
return nil, err
}
// Use a map to group preodds by fixture
fixtureMap := make(map[string]*domain.EnetpulseFixtureWithPreodds)
for _, row := range dbRows {
// If fixture not yet in map, add it
if _, exists := fixtureMap[row.FixtureID]; !exists {
fixtureMap[row.FixtureID] = &domain.EnetpulseFixtureWithPreodds{
FixtureID: row.FixtureID,
FixtureApiID: row.FixtureID, // same alias used in query
FixtureName: row.FixtureName,
SportFk: row.SportFk,
TournamentFk: row.TournamentFk.String,
TournamentTemplateFk: row.TournamentTemplateFk.String,
TournamentStageFk: row.TournamentStageFk.String,
StartDate: row.StartDate.Time,
StatusType: row.StatusType.String,
StatusDescFk: row.StatusDescFk.String,
RoundTypeFk: row.RoundTypeFk.String,
UpdatesCount: row.FixtureUpdatesCount.Int32,
LastUpdatedAt: row.FixtureLastUpdatedAt.Time,
CreatedAt: row.FixtureCreatedAt.Time,
UpdatedAt: row.FixtureUpdatedAt.Time,
Preodds: []domain.EnetpulsePreodds{}, // initialize slice
}
}
// Add preodds only if it exists (avoid NULL rows)
if row.PreoddsDbID.Valid {
preodds := domain.EnetpulsePreodds{
ID: row.PreoddsDbID.Int64,
PreoddsID: row.PreoddsID.String,
EventFK: row.EventFk.Int64,
OutcomeTypeFK: row.OutcomeTypeFk.Int32,
OutcomeScopeFK: row.OutcomeScopeFk.Int32,
OutcomeSubtypeFK: row.OutcomeSubtypeFk.Int32,
EventParticipantNumber: row.EventParticipantNumber.Int32,
IParam: row.Iparam.String,
IParam2: row.Iparam2.String,
DParam: row.Dparam.String,
DParam2: row.Dparam2.String,
SParam: row.Sparam.String,
UpdatesCount: row.PreoddsUpdatesCount.Int32,
LastUpdatedAt: row.PreoddsLastUpdatedAt.Time,
CreatedAt: row.PreoddsCreatedAt.Time,
UpdatedAt: row.PreoddsUpdatedAt.Time,
}
fixtureMap[row.FixtureID].Preodds = append(fixtureMap[row.FixtureID].Preodds, preodds)
}
}
// Flatten the map into a slice
result := make([]domain.EnetpulseFixtureWithPreodds, 0, len(fixtureMap))
for _, f := range fixtureMap {
result = append(result, *f)
}
return result, nil
}
// func ConvertCreateEnetpulseTournamentStage(stage domain.CreateEnetpulseTournamentStage) dbgen.EnetpulseTournamentStage { // func ConvertCreateEnetpulseTournamentStage(stage domain.CreateEnetpulseTournamentStage) dbgen.EnetpulseTournamentStage {
// return dbgen.EnetpulseTournamentStage{ // return dbgen.EnetpulseTournamentStage{
// StageID: stage.StageID, // StageID: stage.StageID,
@ -356,14 +421,14 @@ func ConvertDBEnetpulseFixture(dbF dbgen.EnetpulseFixture) domain.EnetpulseFixtu
TournamentTemplateName: dbF.TournamentTemplateName.String, TournamentTemplateName: dbF.TournamentTemplateName.String,
SportName: dbF.SportName.String, SportName: dbF.SportName.String,
Gender: dbF.Gender.String, Gender: dbF.Gender.String,
StartDate: dbF.StartDate.Time, StartDate: dbF.StartDate.Time.String(),
StatusType: dbF.StatusType.String, StatusType: dbF.StatusType.String,
StatusDescFK: dbF.StatusDescFk.String, StatusDescFK: dbF.StatusDescFk.String,
RoundTypeFK: dbF.RoundTypeFk.String, RoundTypeFK: dbF.RoundTypeFk.String,
UpdatesCount: int(dbF.UpdatesCount.Int32), UpdatesCount: fmt.Sprintf("%v", dbF.UpdatesCount),
LastUpdatedAt: dbF.LastUpdatedAt.Time, LastUpdatedAt: dbF.LastUpdatedAt.Time.String(),
CreatedAt: dbF.CreatedAt.Time, // CreatedAt: dbF.CreatedAt.Time,
UpdatedAt: dbF.UpdatedAt.Time, // UpdatedAt: dbF.UpdatedAt.Time,
} }
} }
@ -672,17 +737,17 @@ func ConvertCreateEnetpulsePreodds(p domain.CreateEnetpulsePreodds) (dbgen.Creat
func ConvertDBEnetpulsePreodds(dbP dbgen.EnetpulsePreodd) domain.EnetpulsePreodds { func ConvertDBEnetpulsePreodds(dbP dbgen.EnetpulsePreodd) domain.EnetpulsePreodds {
return domain.EnetpulsePreodds{ return domain.EnetpulsePreodds{
PreoddsID: dbP.PreoddsID, PreoddsID: dbP.PreoddsID,
EventFK: fmt.Sprintf("%v", dbP.EventFk), EventFK: dbP.EventFk,
OutcomeTypeFK: fmt.Sprintf("%v", dbP.OutcomeTypeFk), OutcomeTypeFK: dbP.OutcomeTypeFk.Int32,
OutcomeScopeFK: fmt.Sprintf("%v", dbP.OutcomeScopeFk), OutcomeScopeFK: dbP.OutcomeScopeFk.Int32,
OutcomeSubtypeFK: fmt.Sprintf("%v", dbP.OutcomeSubtypeFk), OutcomeSubtypeFK: dbP.OutcomeSubtypeFk.Int32,
EventParticipantNumber: int(dbP.EventParticipantNumber.Int32), EventParticipantNumber: dbP.EventParticipantNumber.Int32,
IParam: dbP.Iparam.String, IParam: dbP.Iparam.String,
IParam2: dbP.Iparam2.String, IParam2: dbP.Iparam2.String,
DParam: dbP.Dparam.String, DParam: dbP.Dparam.String,
DParam2: dbP.Dparam2.String, DParam2: dbP.Dparam2.String,
SParam: dbP.Sparam.String, SParam: dbP.Sparam.String,
UpdatesCount: int(dbP.UpdatesCount.Int32), UpdatesCount: dbP.UpdatesCount.Int32,
LastUpdatedAt: dbP.LastUpdatedAt.Time, LastUpdatedAt: dbP.LastUpdatedAt.Time,
CreatedAt: dbP.CreatedAt.Time, CreatedAt: dbP.CreatedAt.Time,
UpdatedAt: dbP.UpdatedAt.Time, UpdatedAt: dbP.UpdatedAt.Time,

View File

@ -3,7 +3,6 @@ package repository
import ( import (
"context" "context"
"fmt" "fmt"
"math"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -23,11 +22,15 @@ func (s *Store) GetLiveEventIDs(ctx context.Context) ([]int64, error) {
func (s *Store) GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) { func (s *Store) GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) {
events, err := s.queries.GetAllEvents(ctx, dbgen.GetAllEventsParams{ events, err := s.queries.GetAllEvents(ctx, dbgen.GetAllEventsParams{
LeagueID: filter.LeagueID.ToPG(), LeagueID: filter.LeagueID.ToPG(),
SportID: filter.SportID.ToPG(), SportID: filter.SportID.ToPG(),
Query: filter.Query.ToPG(), Query: filter.Query.ToPG(),
Limit: filter.Limit.ToPG(), Limit: filter.Limit.ToPG(),
Offset: filter.Offset.ToPG(), Offset: pgtype.Int4{
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
FirstStartTime: filter.FirstStartTime.ToPG(), FirstStartTime: filter.FirstStartTime.ToPG(),
LastStartTime: filter.LastStartTime.ToPG(), LastStartTime: filter.LastStartTime.ToPG(),
CountryCode: filter.CountryCode.ToPG(), CountryCode: filter.CountryCode.ToPG(),
@ -55,18 +58,20 @@ func (s *Store) GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]
return nil, 0, err return nil, 0, err
} }
numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value)) return domain.ConvertDBEvents(events), totalCount, nil
return domain.ConvertDBEvents(events), int64(numberOfPages), nil
} }
func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) { func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) {
events, err := s.queries.GetEventsWithSettings(ctx, dbgen.GetEventsWithSettingsParams{ events, err := s.queries.GetEventsWithSettings(ctx, dbgen.GetEventsWithSettingsParams{
CompanyID: companyID, CompanyID: companyID,
LeagueID: filter.LeagueID.ToPG(), LeagueID: filter.LeagueID.ToPG(),
SportID: filter.SportID.ToPG(), SportID: filter.SportID.ToPG(),
Query: filter.Query.ToPG(), Query: filter.Query.ToPG(),
Limit: filter.Limit.ToPG(), Limit: filter.Limit.ToPG(),
Offset: filter.Offset.ToPG(), Offset: pgtype.Int4{
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
FirstStartTime: filter.FirstStartTime.ToPG(), FirstStartTime: filter.FirstStartTime.ToPG(),
LastStartTime: filter.LastStartTime.ToPG(), LastStartTime: filter.LastStartTime.ToPG(),
CountryCode: filter.CountryCode.ToPG(), CountryCode: filter.CountryCode.ToPG(),
@ -99,8 +104,6 @@ func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filt
return nil, 0, err return nil, 0, err
} }
numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value))
result := make([]domain.EventWithSettings, len(events)) result := make([]domain.EventWithSettings, len(events))
for i, event := range events { for i, event := range events {
@ -123,6 +126,9 @@ func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filt
StartTime: event.StartTime.Time.UTC(), StartTime: event.StartTime.Time.UTC(),
Source: domain.EventSource(event.Source), Source: domain.EventSource(event.Source),
Status: domain.EventStatus(event.Status), Status: domain.EventStatus(event.Status),
TotalOddOutcomes: event.TotalOutcomes,
SourceEventID: event.SourceEventID,
WinningUpperLimit: event.WinningUpperLimit,
IsFeatured: event.IsFeatured, IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored, IsMonitored: event.IsMonitored,
IsActive: event.IsActive, IsActive: event.IsActive,
@ -155,7 +161,7 @@ func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filt
} }
} }
return result, int64(numberOfPages), nil return result, totalCount, nil
} }
func (s *Store) GetEventByID(ctx context.Context, ID int64) (domain.BaseEvent, error) { func (s *Store) GetEventByID(ctx context.Context, ID int64) (domain.BaseEvent, error) {
event, err := s.queries.GetEventByID(ctx, ID) event, err := s.queries.GetEventByID(ctx, ID)
@ -204,6 +210,9 @@ func (s *Store) GetEventWithSettingByID(ctx context.Context, ID int64, companyID
StartTime: event.StartTime.Time.UTC(), StartTime: event.StartTime.Time.UTC(),
Source: domain.EventSource(event.Source), Source: domain.EventSource(event.Source),
Status: domain.EventStatus(event.Status), Status: domain.EventStatus(event.Status),
TotalOddOutcomes: event.TotalOutcomes,
SourceEventID: event.SourceEventID,
WinningUpperLimit: event.WinningUpperLimit,
IsFeatured: event.IsFeatured, IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored, IsMonitored: event.IsMonitored,
IsActive: event.IsActive, IsActive: event.IsActive,
@ -281,10 +290,13 @@ func (s *Store) UpdateEventMonitored(ctx context.Context, eventID int64, IsMonit
}) })
} }
func (s *Store) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error { func (s *Store) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error {
return s.queries.SaveEventSettings(ctx, domain.ConvertUpdateEventSettings(event)) return s.queries.SaveTenantEventSettings(ctx, domain.ConvertUpdateTenantEventSettings(event))
} }
func (s *Store) UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error {
return s.queries.UpdateGlobalEventSettings(ctx, domain.ConvertUpdateGlobalEventSettings(event))
}
func (s *Store) DeleteEvent(ctx context.Context, eventID int64) error { func (s *Store) DeleteEvent(ctx context.Context, eventID int64) error {
err := s.queries.DeleteEvent(ctx, eventID) err := s.queries.DeleteEvent(ctx, eventID)
if err != nil { if err != nil {

View File

@ -13,10 +13,10 @@ func (s *Store) SaveLeague(ctx context.Context, league domain.CreateLeague) erro
} }
func (s *Store) SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error { func (s *Store) SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error {
return s.queries.InsertLeagueSettings(ctx, domain.ConvertCreateLeagueSettings(leagueSettings)) return s.queries.SaveLeagueSettings(ctx, domain.ConvertCreateLeagueSettings(leagueSettings))
} }
func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error) { func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague,int64, error) {
l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{ l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{
Query: filter.Query.ToPG(), Query: filter.Query.ToPG(),
CountryCode: filter.CountryCode.ToPG(), CountryCode: filter.CountryCode.ToPG(),
@ -31,10 +31,17 @@ func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) (
}, },
}) })
if err != nil { if err != nil {
return nil, err return nil, 0, err
} }
return domain.ConvertDBBaseLeagues(l), nil total, err := s.queries.GetTotalLeagues(ctx, dbgen.GetTotalLeaguesParams{
Query: filter.Query.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
SportID: filter.SportID.ToPG(),
IsActive: filter.IsActive.ToPG(),
})
return domain.ConvertDBBaseLeagues(l), total, nil
} }
func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error) { func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error) {
@ -85,3 +92,7 @@ func (s *Store) CheckLeagueSupport(ctx context.Context, leagueID int64, companyI
func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error { func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {
return s.queries.UpdateLeague(ctx, domain.ConvertUpdateLeague(league)) return s.queries.UpdateLeague(ctx, domain.ConvertUpdateLeague(league))
} }
func (s *Store) UpdateGlobalLeagueSettings(ctx context.Context, league domain.UpdateGlobalLeagueSettings) error {
return s.queries.UpdateGlobalLeagueSettings(ctx, domain.ConvertUpdateGlobalLeagueSetting(league))
}

View File

@ -14,12 +14,13 @@ import (
type NotificationRepository interface { type NotificationRepository interface {
CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error) CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error)
UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error) UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error)
GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error)
ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error) ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error)
ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error)
CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error)
GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error)
GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error) GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error)
DeleteOldNotifications(ctx context.Context) error
} }
type Repository struct { type Repository struct {
@ -69,6 +70,8 @@ func (r *Repository) CreateNotification(ctx context.Context, notification *domai
Payload: marshalPayload(notification.Payload), Payload: marshalPayload(notification.Payload),
Priority: priority, Priority: priority,
Timestamp: pgtype.Timestamptz{Time: notification.Timestamp, Valid: true}, Timestamp: pgtype.Timestamptz{Time: notification.Timestamp, Valid: true},
Expires: pgtype.Timestamptz{Time: notification.Expires, Valid: true},
Img: pgtype.Text{String: notification.Image, Valid: notification.Image != ""},
Metadata: notification.Metadata, Metadata: notification.Metadata,
} }
@ -113,7 +116,7 @@ func (r *Repository) GetUserNotifications(ctx context.Context, recipientID int64
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications)) var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications))
for _, dbNotif := range dbNotifications { for _, dbNotif := range dbNotifications {
domainNotif := r.mapDBToDomain(&dbNotif) domainNotif := r.mapDBToDomain(&dbNotif)
@ -160,6 +163,10 @@ func (r *Repository) ListRecipientIDs(ctx context.Context, receiver domain.Notif
return r.store.queries.ListRecipientIDsByReceiver(ctx, string(receiver)) return r.store.queries.ListRecipientIDsByReceiver(ctx, string(receiver))
} }
func (s *Repository) DeleteOldNotifications(ctx context.Context) error {
return s.store.queries.DeleteOldNotifications(ctx)
}
func (r *Repository) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification { func (r *Repository) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification {
var errorSeverity domain.NotificationErrorSeverity var errorSeverity domain.NotificationErrorSeverity
if dbNotif.ErrorSeverity.Valid { if dbNotif.ErrorSeverity.Valid {
@ -199,6 +206,8 @@ func (r *Repository) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notifica
Payload: payload, Payload: payload,
Priority: priority, Priority: priority,
Timestamp: dbNotif.Timestamp.Time, Timestamp: dbNotif.Timestamp.Time,
Expires: dbNotif.Expires.Time,
Image: dbNotif.Img.String,
Metadata: dbNotif.Metadata, Metadata: dbNotif.Metadata,
} }
} }

View File

@ -180,16 +180,17 @@ func (s *Store) GetOddsWithSettingsByMarketID(ctx context.Context, marketID int6
} }
converted := domain.OddMarketWithSettings{ converted := domain.OddMarketWithSettings{
ID: odds.ID, ID: odds.ID,
EventID: odds.EventID, EventID: odds.EventID,
MarketType: odds.MarketType, MarketType: odds.MarketType,
MarketName: odds.MarketName, MarketName: odds.MarketName,
MarketCategory: odds.MarketCategory, MarketCategory: odds.MarketCategory,
MarketID: odds.MarketID, MarketID: odds.MarketID,
RawOdds: rawOdds, NumberOfOutcomes: odds.NumberOfOutcomes,
FetchedAt: odds.FetchedAt.Time, RawOdds: rawOdds,
ExpiresAt: odds.ExpiresAt.Time, FetchedAt: odds.FetchedAt.Time,
IsActive: odds.IsActive, ExpiresAt: odds.ExpiresAt.Time,
IsActive: odds.IsActive,
} }
return converted, nil return converted, nil
} }
@ -221,16 +222,17 @@ func (s *Store) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID
} }
converted := domain.OddMarketWithSettings{ converted := domain.OddMarketWithSettings{
ID: odds.ID, ID: odds.ID,
EventID: odds.EventID, EventID: odds.EventID,
MarketType: odds.MarketType, MarketType: odds.MarketType,
MarketName: odds.MarketName, MarketName: odds.MarketName,
MarketCategory: odds.MarketCategory, MarketCategory: odds.MarketCategory,
MarketID: odds.MarketID, MarketID: odds.MarketID,
RawOdds: rawOdds, NumberOfOutcomes: odds.NumberOfOutcomes,
FetchedAt: odds.FetchedAt.Time, RawOdds: rawOdds,
ExpiresAt: odds.ExpiresAt.Time, FetchedAt: odds.FetchedAt.Time,
IsActive: odds.IsActive, ExpiresAt: odds.ExpiresAt.Time,
IsActive: odds.IsActive,
} }
return converted, nil return converted, nil
@ -239,17 +241,11 @@ func (s *Store) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID
func (s *Store) GetOddsByEventID(ctx context.Context, eventID int64, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) { func (s *Store) GetOddsByEventID(ctx context.Context, eventID int64, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) {
odds, err := s.queries.GetOddsByEventID(ctx, dbgen.GetOddsByEventIDParams{ odds, err := s.queries.GetOddsByEventID(ctx, dbgen.GetOddsByEventIDParams{
EventID: eventID, EventID: eventID,
Status: filter.Status.ToPG(),
IsLive: filter.IsLive.ToPG(),
Limit: filter.Limit.ToPG(), Limit: filter.Limit.ToPG(),
Offset: filter.Offset.ToPG(), Offset: filter.Offset.ToPG(),
IsLive: pgtype.Bool{ Source: pgtype.Text{},
Bool: false,
Valid: true,
},
Status: pgtype.Text{
String: string(domain.STATUS_PENDING),
Valid: true,
},
Source: pgtype.Text{},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -293,16 +289,17 @@ func (s *Store) GetOddsWithSettingsByEventID(ctx context.Context, eventID int64,
} }
result[i] = domain.OddMarketWithSettings{ result[i] = domain.OddMarketWithSettings{
ID: o.ID, ID: o.ID,
EventID: o.EventID, EventID: o.EventID,
MarketType: o.MarketType, MarketType: o.MarketType,
MarketName: o.MarketName, MarketName: o.MarketName,
MarketCategory: o.MarketCategory, MarketCategory: o.MarketCategory,
MarketID: o.MarketID, MarketID: o.MarketID,
RawOdds: rawOdds, NumberOfOutcomes: o.NumberOfOutcomes,
FetchedAt: o.FetchedAt.Time, RawOdds: rawOdds,
ExpiresAt: o.ExpiresAt.Time, FetchedAt: o.FetchedAt.Time,
IsActive: o.IsActive, ExpiresAt: o.ExpiresAt.Time,
IsActive: o.IsActive,
} }
} }
@ -322,3 +319,21 @@ func (s *Store) SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketS
} }
return s.queries.SaveOddSettings(ctx, res) return s.queries.SaveOddSettings(ctx, res)
} }
func (s *Store) UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error {
return s.queries.UpdateGlobalOddsSetting(ctx, dbgen.UpdateGlobalOddsSettingParams{
ID: odd.OddMarketID,
DefaultIsActive: odd.IsActive.ToPG(),
})
}
func (s *Store) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error {
return s.queries.DeleteAllCompanyOddsSetting(ctx, companyID)
}
func (s *Store) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error {
return s.queries.DeleteCompanyOddsSettingByOddMarketID(ctx, dbgen.DeleteCompanyOddsSettingByOddMarketIDParams{
CompanyID: companyID,
OddsMarketID: oddMarketID,
})
}

View File

@ -10,13 +10,14 @@ import (
func convertRaffleOutcome(raffle dbgen.Raffle) domain.Raffle { func convertRaffleOutcome(raffle dbgen.Raffle) domain.Raffle {
return domain.Raffle{ return domain.Raffle{
ID: raffle.ID, ID: raffle.ID,
CompanyID: raffle.CompanyID, CompanyID: raffle.CompanyID,
Name: raffle.Name, Name: raffle.Name,
CreatedAt: raffle.CreatedAt.Time, CreatedAt: raffle.CreatedAt.Time,
ExpiresAt: raffle.ExpiresAt.Time, ExpiresAt: raffle.ExpiresAt.Time,
Type: raffle.Type, TicketLimit: raffle.TicketLimit,
Status: raffle.Status, Type: raffle.Type,
Status: raffle.Status,
} }
} }
@ -48,7 +49,8 @@ func convertCreateRaffle(raffle domain.CreateRaffle) dbgen.CreateRaffleParams {
Time: *raffle.ExpiresAt, Time: *raffle.ExpiresAt,
Valid: true, Valid: true,
}, },
Type: raffle.Type, TicketLimit: raffle.TicketLimit,
Type: raffle.Type,
} }
} }
@ -191,3 +193,18 @@ func (s *Store) CheckValidSportRaffleFilter(ctx context.Context, raffleID int32,
return res, nil return res, nil
} }
func (s *Store) GetRaffleTicketLimit(ctx context.Context, raffleID int32) (int32, error) {
return s.queries.GetRaffleTicketLimit(ctx, raffleID)
}
func (s *Store) GetRaffleTicketCount(ctx context.Context, raffleID, userID int32) (int64, error) {
return s.queries.GetRaffleTicketCount(ctx, dbgen.GetRaffleTicketCountParams{
RaffleID: raffleID,
UserID: userID,
})
}
func (s *Store) CheckSportRaffleHasFilter(ctx context.Context, raffleID int32) (bool, error) {
return s.queries.CheckSportRaffleHasFilter(ctx, raffleID)
}

View File

@ -35,6 +35,7 @@ func convertDBShopBetDetail(bet dbgen.ShopBetDetail) domain.ShopBetDetail {
CompanyID: bet.CompanyID, CompanyID: bet.CompanyID,
FullName: bet.CustomerFullName, FullName: bet.CustomerFullName,
PhoneNumber: bet.CustomerPhoneNumber, PhoneNumber: bet.CustomerPhoneNumber,
FastCode: bet.FastCode,
CashoutID: bet.CashoutID, CashoutID: bet.CashoutID,
CashedOut: bet.CashedOut, CashedOut: bet.CashedOut,
BetID: bet.BetID, BetID: bet.BetID,
@ -63,7 +64,7 @@ func (s *Store) CreateShopBet(ctx context.Context, bet domain.CreateShopBet) (do
if err != nil { if err != nil {
return domain.ShopBet{}, err return domain.ShopBet{}, err
} }
return convertDBShopBet(newShopBet), err return convertDBShopBet(newShopBet), err
} }
@ -108,7 +109,7 @@ func (s *Store) GetShopBetByID(ctx context.Context, id int64) (domain.ShopBetDet
fmt.Printf("GetShopBetByID Repo BetID %d err %v \n", id, err.Error()) fmt.Printf("GetShopBetByID Repo BetID %d err %v \n", id, err.Error())
return domain.ShopBetDetail{}, err return domain.ShopBetDetail{}, err
} }
return convertDBShopBetDetail(bet), nil return convertDBShopBetDetail(bet), nil
} }

View File

@ -111,7 +111,7 @@ func (s *Store) GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]do
Valid: filter.PageSize.Valid, Valid: filter.PageSize.Valid,
}, },
Offset: pgtype.Int4{ Offset: pgtype.Int4{
Int32: int32(filter.Page.Value), Int32: int32(filter.Page.Value * filter.PageSize.Value),
Valid: filter.Page.Valid, Valid: filter.Page.Valid,
}, },
Query: pgtype.Text{ Query: pgtype.Text{

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
"fmt"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -30,7 +29,7 @@ type VirtualGameRepository interface {
RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error
ListFavoriteGames(ctx context.Context, userID int64) ([]int64, error) ListFavoriteGames(ctx context.Context, userID int64) ([]int64, error)
GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) // GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error) GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error)
CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error
@ -255,36 +254,36 @@ func (r *VirtualGameRepo) UpdateVirtualGameTransactionStatus(ctx context.Context
}) })
} }
func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) { // func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) {
query := `SELECT // query := `SELECT
COUNT(*) as total, // COUNT(*) as total,
COUNT(CASE WHEN is_active = true THEN 1 END) as active, // COUNT(CASE WHEN is_active = true THEN 1 END) as active,
COUNT(CASE WHEN is_active = false THEN 1 END) as inactive // COUNT(CASE WHEN is_active = false THEN 1 END) as inactive
FROM virtual_games` // FROM virtual_games`
args := []interface{}{} // args := []interface{}{}
argPos := 1 // argPos := 1
// Add filters if provided // // Add filters if provided
if filter.StartTime.Valid { // if filter.StartTime.Valid {
query += fmt.Sprintf(" WHERE created_at >= $%d", argPos) // query += fmt.Sprintf(" WHERE created_at >= $%d", argPos)
args = append(args, filter.StartTime.Value) // args = append(args, filter.StartTime.Value)
argPos++ // argPos++
} // }
if filter.EndTime.Valid { // if filter.EndTime.Valid {
query += fmt.Sprintf(" AND created_at <= $%d", argPos) // query += fmt.Sprintf(" AND created_at <= $%d", argPos)
args = append(args, filter.EndTime.Value) // args = append(args, filter.EndTime.Value)
argPos++ // argPos++
} // }
row := r.store.conn.QueryRow(ctx, query, args...) // row := r.store.conn.QueryRow(ctx, query, args...)
err = row.Scan(&total, &active, &inactive) // err = row.Scan(&total, &active, &inactive)
if err != nil { // if err != nil {
return 0, 0, 0, fmt.Errorf("failed to get game counts: %w", err) // return 0, 0, 0, fmt.Errorf("failed to get game counts: %w", err)
} // }
return total, active, inactive, nil // return total, active, inactive, nil
} // }
func (r *VirtualGameRepo) GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error) { func (r *VirtualGameRepo) GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error) {
query := `SELECT game_id FROM virtual_game_histories WHERE user_id = $1 AND transaction_type = 'BET' ORDER BY created_at DESC LIMIT 100` query := `SELECT game_id FROM virtual_game_histories WHERE user_id = $1 AND transaction_type = 'BET' ORDER BY created_at DESC LIMIT 100`
@ -315,4 +314,3 @@ func (r *VirtualGameRepo) ListAllVirtualGames(ctx context.Context, arg dbgen.Get
func (r *VirtualGameRepo) RemoveAllVirtualGames(ctx context.Context) error { func (r *VirtualGameRepo) RemoveAllVirtualGames(ctx context.Context) error {
return r.store.queries.DeleteAllVirtualGames(ctx) return r.store.queries.DeleteAllVirtualGames(ctx)
} }

View File

@ -71,6 +71,9 @@ func convertDBGetCustomerWallet(customerWallet dbgen.CustomerWalletDetail) domai
func (s *Store) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) { func (s *Store) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) {
newWallet, err := s.queries.CreateWallet(ctx, convertCreateWallet(wallet)) newWallet, err := s.queries.CreateWallet(ctx, convertCreateWallet(wallet))
if err != nil { if err != nil {
if IsUniqueViolation(err) {
return domain.Wallet{}, domain.ErrWalletIDDuplicate
}
return domain.Wallet{}, err return domain.Wallet{}, err
} }
return convertDBWallet(newWallet), nil return convertDBWallet(newWallet), nil

View File

@ -231,7 +231,7 @@ func (s *Service) SendAdminErrorNotification(ctx context.Context, betID int64, s
for _, user := range users { for _, user := range users {
for _, channel := range []domain.DeliveryChannel{ for _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp, domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail, // domain.DeliveryChannelEmail,
} { } {
n := newBetResultNotification(user.ID, domain.NotificationLevelError, channel, headline, message, map[string]any{ n := newBetResultNotification(user.ID, domain.NotificationLevelError, channel, headline, message, map[string]any{
"status": status, "status": status,
@ -247,7 +247,7 @@ func (s *Service) SendAdminErrorNotification(ctx context.Context, betID int64, s
} }
func (s *Service) SendAdminLargeBetNotification(ctx context.Context, betID int64, totalWinnings float32, extra string, companyID int64) error { func (s *Service) SendAdminLargeBetNotification(ctx context.Context, betID int64, totalWinnings float32, extra string, companyID int64) error {
headline := fmt.Sprintf("SYSTEM WARNING: High Risk Bet", betID, totalWinnings) headline := "SYSTEM WARNING: High Risk Bet"
message := fmt.Sprintf("Bet #%d has been created with %v payout", betID, totalWinnings) message := fmt.Sprintf("Bet #%d has been created with %v payout", betID, totalWinnings)
super_admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ super_admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{
@ -283,7 +283,7 @@ func (s *Service) SendAdminLargeBetNotification(ctx context.Context, betID int64
for _, user := range users { for _, user := range users {
for _, channel := range []domain.DeliveryChannel{ for _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp, domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail, // domain.DeliveryChannelEmail,
} { } {
raw, _ := json.Marshal(map[string]any{ raw, _ := json.Marshal(map[string]any{
"winnings": totalWinnings, "winnings": totalWinnings,

View File

@ -15,6 +15,7 @@ type BetStore interface {
GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, int64, error) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, int64, error)
GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error)
GetBetByFastCode(ctx context.Context, fastcode string) (domain.GetBet, error) GetBetByFastCode(ctx context.Context, fastcode string) (domain.GetBet, error)
GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error)
GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error)
GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error) GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error)
GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (int64, error) GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (int64, error)
@ -25,7 +26,8 @@ type BetStore interface {
UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)
UpdateBetOutcomeStatusByBetID(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) UpdateBetOutcomeStatusByBetID(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)
UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error)
UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error)
BulkUpdateBetOutcomeStatusForOddIds(ctx context.Context, oddID []int64, status domain.OutcomeStatus) error
GetBetSummary(ctx context.Context, filter domain.ReportFilter) ( GetBetSummary(ctx context.Context, filter domain.ReportFilter) (
totalStakes domain.Currency, totalStakes domain.Currency,
totalBets int64, totalBets int64,

View File

@ -35,8 +35,10 @@ var (
ErrGenerateRandomOutcome = errors.New("failed to generate any random outcome for events") ErrGenerateRandomOutcome = errors.New("failed to generate any random outcome for events")
ErrOutcomesNotCompleted = errors.New("some bet outcomes are still pending") ErrOutcomesNotCompleted = errors.New("some bet outcomes are still pending")
ErrEventHasBeenRemoved = errors.New("event has been removed") ErrEventHasBeenRemoved = errors.New("event has been removed")
ErrEventHasBeenDisabled = errors.New("event has been disabled")
ErrEventHasNotEnded = errors.New("event has not ended yet") ErrEventHasNotEnded = errors.New("event has not ended yet")
ErrOddHasBeenDisabled = errors.New("odd has been disabled")
ErrRawOddInvalid = errors.New("prematch Raw Odd is Invalid") ErrRawOddInvalid = errors.New("prematch Raw Odd is Invalid")
ErrBranchIDRequired = errors.New("branch ID required for this role") ErrBranchIDRequired = errors.New("branch ID required for this role")
ErrOutcomeLimit = errors.New("too many outcomes on a single bet") ErrOutcomeLimit = errors.New("too many outcomes on a single bet")
@ -46,6 +48,8 @@ var (
ErrInvalidAmount = errors.New("invalid amount") ErrInvalidAmount = errors.New("invalid amount")
ErrBetAmountTooHigh = errors.New("cannot create a bet with an amount above limit") ErrBetAmountTooHigh = errors.New("cannot create a bet with an amount above limit")
ErrBetWinningTooHigh = errors.New("total Winnings over set limit") ErrBetWinningTooHigh = errors.New("total Winnings over set limit")
ErrCompanyDeductedPercentInvalid = errors.New("invalid company deducted percentage")
) )
type Service struct { type Service struct {
@ -106,10 +110,10 @@ func (s *Service) GenerateCashoutID() (string, error) {
return string(result), nil return string(result), nil
} }
func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketID int64, oddID int64) (domain.CreateBetOutcome, error) { func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketID int64, oddID int64, companyID int64) (domain.CreateBetOutcome, error) {
oddIDStr := strconv.FormatInt(oddID, 10) oddIDStr := strconv.FormatInt(oddID, 10)
event, err := s.eventSvc.GetEventByID(ctx, eventID) event, err := s.eventSvc.GetEventWithSettingByID(ctx, eventID, companyID)
if err != nil { if err != nil {
s.mongoLogger.Error("failed to fetch upcoming event by ID", s.mongoLogger.Error("failed to fetch upcoming event by ID",
zap.Int64("event_id", eventID), zap.Int64("event_id", eventID),
@ -118,6 +122,14 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
return domain.CreateBetOutcome{}, ErrEventHasBeenRemoved return domain.CreateBetOutcome{}, ErrEventHasBeenRemoved
} }
if !event.IsActive {
s.mongoLogger.Warn("attempting to create bet with disabled event",
zap.Int64("event_id", eventID),
zap.Error(err),
)
return domain.CreateBetOutcome{}, ErrEventHasBeenDisabled
}
currentTime := time.Now() currentTime := time.Now()
if event.StartTime.Before(currentTime) { if event.StartTime.Before(currentTime) {
s.mongoLogger.Error("event has already started", s.mongoLogger.Error("event has already started",
@ -128,7 +140,7 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
return domain.CreateBetOutcome{}, ErrEventHasNotEnded return domain.CreateBetOutcome{}, ErrEventHasNotEnded
} }
odds, err := s.prematchSvc.GetOddsByMarketID(ctx, marketID, eventID) odds, err := s.prematchSvc.GetOddsWithSettingsByMarketID(ctx, marketID, eventID, companyID)
if err != nil { if err != nil {
s.mongoLogger.Error("failed to get raw odds by market ID", s.mongoLogger.Error("failed to get raw odds by market ID",
zap.Int64("event_id", eventID), zap.Int64("event_id", eventID),
@ -138,6 +150,15 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
return domain.CreateBetOutcome{}, err return domain.CreateBetOutcome{}, err
} }
if !odds.IsActive {
s.mongoLogger.Error("failed to get raw odds by market ID",
zap.Int64("event_id", eventID),
zap.Int64("market_id", marketID),
zap.Error(err),
)
return domain.CreateBetOutcome{}, ErrOddHasBeenDisabled
}
type rawOddType struct { type rawOddType struct {
ID string ID string
Name string Name string
@ -255,7 +276,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
var totalOdds float32 = 1 var totalOdds float32 = 1
for _, outcomeReq := range req.Outcomes { for _, outcomeReq := range req.Outcomes {
newOutcome, err := s.GenerateBetOutcome(ctx, outcomeReq.EventID, outcomeReq.MarketID, outcomeReq.OddID) newOutcome, err := s.GenerateBetOutcome(ctx, outcomeReq.EventID, outcomeReq.MarketID, outcomeReq.OddID, companyID)
if err != nil { if err != nil {
s.mongoLogger.Error("failed to generate outcome", s.mongoLogger.Error("failed to generate outcome",
zap.Int64("event_id", outcomeReq.EventID), zap.Int64("event_id", outcomeReq.EventID),
@ -303,8 +324,9 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
} }
fastCode := helpers.GenerateFastCode() fastCode := helpers.GenerateFastCode()
accumulator := calculateAccumulator(len(outcomes)) // accumulator := calculateAccumulator(len(outcomes))
amount := req.Amount + (req.Amount * accumulator) // amount := req.Amount + (req.Amount * accumulator)
amount := req.Amount
newBet := domain.CreateBet{ newBet := domain.CreateBet{
Amount: domain.ToCurrency(amount), Amount: domain.ToCurrency(amount),
@ -524,7 +546,27 @@ func (s *Service) DeductBetFromBranchWallet(ctx context.Context, amount float32,
return err return err
} }
if company.DeductedPercentage > 1 {
s.mongoLogger.Error("Invalid company deducted percentage",
zap.Int64("wallet_id", walletID),
zap.Float32("amount", company.DeductedPercentage),
zap.Error(err),
)
return ErrCompanyDeductedPercentInvalid
}
// This is the amount that we take from a company/tenant when they
// create a bet. I.e. if its 5% (0.05), then thats the percentage we take every
deductedAmount := amount * company.DeductedPercentage deductedAmount := amount * company.DeductedPercentage
if deductedAmount == 0 {
s.mongoLogger.Fatal("Amount",
zap.Int64("wallet_id", walletID),
zap.Float32("amount", deductedAmount),
zap.Error(err),
)
return err
}
_, err = s.walletSvc.DeductFromWallet(ctx, _, err = s.walletSvc.DeductFromWallet(ctx,
walletID, domain.ToCurrency(deductedAmount), domain.ValidInt64{ walletID, domain.ToCurrency(deductedAmount), domain.ValidInt64{
Value: userID, Value: userID,
@ -848,6 +890,9 @@ func (s *Service) GetBetOutcomeByBetID(ctx context.Context, UserID int64) ([]dom
return s.betStore.GetBetOutcomeByBetID(ctx, UserID) return s.betStore.GetBetOutcomeByBetID(ctx, UserID)
} }
func (s *Service) GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error) {
return s.betStore.GetBetOutcomeViewByEventID(ctx, eventID, filter)
}
func (s *Service) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) { func (s *Service) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) {
return s.betStore.GetBetOutcomeByEventID(ctx, eventID, is_filtered) return s.betStore.GetBetOutcomeByEventID(ctx, eventID, is_filtered)
} }
@ -1076,6 +1121,32 @@ func (s *Service) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID in
return outcomes, nil return outcomes, nil
} }
func (s *Service) UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) {
outcomes, err := s.betStore.UpdateBetOutcomeStatusForOddId(ctx, oddID, status)
if err != nil {
s.mongoLogger.Error("failed to update bet outcome status",
zap.Int64("oddID", oddID),
zap.Error(err),
)
return nil, err
}
return outcomes, nil
}
func (s *Service) BulkUpdateBetOutcomeStatusForOddIds(ctx context.Context, oddID []int64, status domain.OutcomeStatus) error {
err := s.betStore.BulkUpdateBetOutcomeStatusForOddIds(ctx, oddID, status)
if err != nil {
s.mongoLogger.Error("failed to update bet outcome status by oddIds",
zap.Int64s("oddID", oddID),
zap.Error(err),
)
return err
}
return nil
}
func (s *Service) SetBetToRemoved(ctx context.Context, id int64) error { func (s *Service) SetBetToRemoved(ctx context.Context, id int64) error {
_, err := s.betStore.UpdateBetOutcomeStatusByBetID(ctx, id, domain.OUTCOME_STATUS_VOID) _, err := s.betStore.UpdateBetOutcomeStatusByBetID(ctx, id, domain.OUTCOME_STATUS_VOID)
if err != nil { if err != nil {

View File

@ -12,7 +12,7 @@ type BranchStore interface {
GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error)
GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error)
GetAllBranches(ctx context.Context, filter domain.BranchFilter) ([]domain.BranchDetail, error) GetAllBranches(ctx context.Context, filter domain.BranchFilter) ([]domain.BranchDetail, error)
SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error) SearchBranchByName(ctx context.Context, name string, companyID domain.ValidInt64) ([]domain.BranchDetail, error)
UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error) UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error)
DeleteBranch(ctx context.Context, id int64) error DeleteBranch(ctx context.Context, id int64) error
CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error

View File

@ -54,8 +54,8 @@ func (s *Service) GetAllSupportedOperations(ctx context.Context) ([]domain.Suppo
return s.branchStore.GetAllSupportedOperations(ctx) return s.branchStore.GetAllSupportedOperations(ctx)
} }
func (s *Service) SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error) { func (s *Service) SearchBranchByName(ctx context.Context, name string, companyID domain.ValidInt64) ([]domain.BranchDetail, error) {
return s.branchStore.SearchBranchByName(ctx, name) return s.branchStore.SearchBranchByName(ctx, name, companyID)
} }
func (s *Service) UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error) { func (s *Service) UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error) {
return s.branchStore.UpdateBranch(ctx, branch) return s.branchStore.UpdateBranch(ctx, branch)

View File

@ -11,8 +11,8 @@ type CompanyStore interface {
GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error) GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error)
SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error)
GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error)
GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error)
UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (error)
DeleteCompany(ctx context.Context, id int64) error DeleteCompany(ctx context.Context, id int64) error
GetCompanyCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) GetCompanyCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)

View File

@ -26,15 +26,15 @@ func (s *Service) GetAllCompanies(ctx context.Context, filter domain.CompanyFilt
func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) { func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) {
return s.companyStore.GetCompanyByID(ctx, id) return s.companyStore.GetCompanyByID(ctx, id)
} }
func (s *Service) GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error){ func (s *Service) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) {
return s.companyStore.GetCompanyIDBySlug(ctx, slug) return s.companyStore.GetCompanyBySlug(ctx, slug)
} }
func (s *Service) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) { func (s *Service) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) {
return s.companyStore.SearchCompanyByName(ctx, name) return s.companyStore.SearchCompanyByName(ctx, name)
} }
func (s *Service) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) { func (s *Service) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (error) {
return s.companyStore.UpdateCompany(ctx, company) return s.companyStore.UpdateCompany(ctx, company)
} }
func (s *Service) DeleteCompany(ctx context.Context, id int64) error { func (s *Service) DeleteCompany(ctx context.Context, id int64) error {

View File

@ -241,7 +241,7 @@ func (s *Service) FetchAndStoreTournamentTemplates(ctx context.Context) error {
} }
} }
fmt.Println("✅ Successfully fetched and stored all tournament templates") // fmt.Println("✅ Successfully fetched and stored all tournament templates")
return nil return nil
} }
@ -367,7 +367,7 @@ func (s *Service) FetchAndStoreTournamentStages(ctx context.Context) error {
for _, t := range tournaments { for _, t := range tournaments {
// Compose URL for each tournament // Compose URL for each tournament
url := fmt.Sprintf( url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament_stage/list/?language_typeFK=3&tz=Europe/Sofia&tournamentFK=%s&username=%s&token=%s", "https://eapi.enetpulse.com/tournament_stage/list/?language_typeFK=3&tz=Europe/Sofia&tournamentFK=%s&username=%s&token=%s",
t.TournamentID, t.TournamentID,
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token, s.cfg.EnetPulseConfig.Token,
@ -460,125 +460,13 @@ func (s *Service) GetAllTournamentStages(ctx context.Context) ([]domain.Enetpuls
} }
func (s *Service) FetchAndStoreFixtures(ctx context.Context, date string) error { func (s *Service) FetchAndStoreFixtures(ctx context.Context, date string) error {
// 1⃣ Fetch all sports from the database // 1⃣ Fetch all sports from DB
sports, err := s.store.GetAllEnetpulseSports(ctx) sports, err := s.store.GetAllEnetpulseSports(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to fetch sports from DB: %w", err) return fmt.Errorf("failed to fetch sports from DB: %w", err)
} }
// Struct for decoding each fixture from API // Define API fixture struct
type Fixture struct {
FixtureID string `json:"id"`
Name string `json:"name"`
SportFK string `json:"sportFK"`
TournamentFK string `json:"tournamentFK"`
TournamentName string `json:"tournament_name"`
StartDate string `json:"startdate"`
StatusType string `json:"status_type"`
HomeTeam string `json:"home_team"`
AwayTeam string `json:"away_team"`
HomeTeamID string `json:"home_team_id"`
AwayTeamID string `json:"away_team_id"`
HomeKitImage string `json:"home_kit_image"`
AwayKitImage string `json:"away_kit_image"`
}
// 2⃣ Loop through each sport
for _, sport := range sports {
if sport.SportID != "1" {
continue
}
url := fmt.Sprintf(
"http://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s&sportFK=%s&language_typeFK=3&date=%s",
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
sport.SportID,
date,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
fmt.Printf("creating fixtures request for sport %s: %v\n", sport.SportID, err)
continue
}
resp, err := s.httpClient.Do(req)
if err != nil {
fmt.Printf("requesting fixtures for sport %s: %v\n", sport.SportID, err)
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("failed to fetch fixtures for sport %s (status %d): %s\n",
sport.SportID, resp.StatusCode, string(body))
continue
}
// 3⃣ Decode API response
var fixturesResp struct {
Events map[string]Fixture `json:"events"`
}
if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
fmt.Printf("decoding fixtures response for sport %s: %v\n", sport.SportID, err)
continue
}
// 4⃣ Iterate over fixtures and store as events
for _, fx := range fixturesResp.Events {
// Conversions
sportID, _ := strconv.Atoi(fx.SportFK)
homeTeamID, _ := strconv.ParseInt(fx.HomeTeamID, 10, 64)
awayTeamID, _ := strconv.ParseInt(fx.AwayTeamID, 10, 64)
leagueID, _ := strconv.ParseInt(fx.TournamentFK, 10, 64)
startDate, _ := time.Parse("2006-01-02 15:04:05", fx.StartDate)
event := domain.CreateEvent{
SourceEventID: fx.FixtureID,
SportID: int32(sportID),
MatchName: fx.Name,
HomeTeam: fx.HomeTeam,
AwayTeam: fx.AwayTeam,
HomeTeamID: homeTeamID,
AwayTeamID: awayTeamID,
HomeTeamImage: fx.HomeKitImage,
AwayTeamImage: fx.AwayKitImage,
LeagueID: leagueID,
LeagueName: fx.TournamentName,
StartTime: startDate,
IsLive: false, // default, can update later from live feed
Status: domain.STATUS_PENDING, // map to enum if needed
Source: "EnetPulse", // custom enum constant
DefaultWinningUpperLimit: 0, // default, can adjust
}
// 5⃣ Save event in DB (UPSERT)
if err := s.store.SaveEvent(ctx, event); err != nil {
fmt.Printf("failed storing event %s: %v\n", fx.FixtureID, err)
continue
}
}
fmt.Printf("✅ Successfully fetched and stored events for sport %s\n", sport.SportID)
break
}
fmt.Println("✅ Completed fetching and storing events for all sports")
return nil
}
func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.EnetpulseFixture, error) {
var allFixtures []domain.EnetpulseFixture
// 1⃣ Fetch all sports from the database
sports, err := s.store.GetAllEnetpulseSports(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch sports from DB: %w", err)
}
// Struct for decoding each fixture from API
type Fixture struct { type Fixture struct {
FixtureID string `json:"id"` FixtureID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@ -591,23 +479,22 @@ func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.Enet
TournamentTemplateName string `json:"tournament_template_name"` TournamentTemplateName string `json:"tournament_template_name"`
SportName string `json:"sport_name"` SportName string `json:"sport_name"`
Gender string `json:"gender"` Gender string `json:"gender"`
StartDate string `json:"startdate"` StartDate string `json:"startdate"` // ISO 8601
StatusType string `json:"status_type"` StatusType string `json:"status_type"`
StatusDescFK string `json:"status_descFK"` StatusDescFK string `json:"status_descFK"`
RoundTypeFK string `json:"round_typeFK"` RoundTypeFK string `json:"round_typeFK"`
UpdatesCount string `json:"n"` UpdatesCount string `json:"n"` // convert to int
LastUpdatedAt string `json:"ut"` LastUpdatedAt string `json:"ut"` // parse to time.Time
} }
// 2⃣ Loop through each sport // 2⃣ Loop through each sport
for _, sport := range sports { for _, sport := range sports {
// Only fetch for sport "1" (Football)
if sport.SportID != "1" { if sport.SportID != "1" {
continue continue
} }
url := fmt.Sprintf( url := fmt.Sprintf(
"http://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s&sportFK=%s&language_typeFK=3&date=%s", "https://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s&sportFK=%s&language_typeFK=3&date=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token, s.cfg.EnetPulseConfig.Token,
sport.SportID, sport.SportID,
@ -616,7 +503,7 @@ func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.Enet
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
fmt.Printf("creating fixtures request for sport %s: %v\n", sport.SportID, err) fmt.Printf("creating request for sport %s: %v\n", sport.SportID, err)
continue continue
} }
@ -639,29 +526,38 @@ func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.Enet
Events map[string]Fixture `json:"events"` Events map[string]Fixture `json:"events"`
} }
if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil { if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
fmt.Printf("decoding fixtures response for sport %s: %v\n", sport.SportID, err) fmt.Printf("decoding fixtures for sport %s: %v\n", sport.SportID, err)
continue continue
} }
// 4⃣ Iterate over fixtures and store them // 4⃣ Iterate and upsert fixtures
for _, fx := range fixturesResp.Events { for _, fx := range fixturesResp.Events {
tournamentFK, _ := strconv.Atoi(fx.TournamentFK) // Parse StartDate and LastUpdatedAt
tournamentTemplateFK, _ := strconv.Atoi(fx.TournamentTemplateFK) startDate, err := time.Parse(time.RFC3339, fx.StartDate)
tournamentStageFK, _ := strconv.Atoi(fx.TournamentStageFK) if err != nil {
statusDescFK, _ := strconv.Atoi(fx.StatusDescFK) fmt.Printf("invalid startDate for fixture %s: %v\n", fx.FixtureID, err)
roundTypeFK, _ := strconv.Atoi(fx.RoundTypeFK) continue
updatesCount, _ := strconv.Atoi(fx.UpdatesCount) }
lastUpdated, err := time.Parse(time.RFC3339, fx.LastUpdatedAt)
if err != nil {
fmt.Printf("invalid lastUpdatedAt for fixture %s: %v\n", fx.FixtureID, err)
continue
}
startDate, _ := time.Parse(time.RFC3339, fx.StartDate) // Convert UpdatesCount
lastUpdatedAt, _ := time.Parse(time.RFC3339, fx.LastUpdatedAt) updatesCount, err := strconv.Atoi(fx.UpdatesCount)
if err != nil {
fmt.Printf("invalid updatesCount for fixture %s: %v\n", fx.FixtureID, err)
updatesCount = 0
}
createFixture := domain.CreateEnetpulseFixture{ fixture := domain.CreateEnetpulseFixture{
FixtureID: fx.FixtureID, FixtureID: fx.FixtureID,
Name: fx.Name, Name: fx.Name,
SportFK: fx.SportFK, SportFK: fx.SportFK,
TournamentFK: strconv.Itoa(tournamentFK), TournamentFK: fx.TournamentFK,
TournamentTemplateFK: strconv.Itoa(tournamentTemplateFK), TournamentTemplateFK: fx.TournamentTemplateFK,
TournamentStageFK: strconv.Itoa(tournamentStageFK), TournamentStageFK: fx.TournamentStageFK,
TournamentStageName: fx.TournamentStageName, TournamentStageName: fx.TournamentStageName,
TournamentName: fx.TournamentName, TournamentName: fx.TournamentName,
TournamentTemplateName: fx.TournamentTemplateName, TournamentTemplateName: fx.TournamentTemplateName,
@ -669,29 +565,150 @@ func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.Enet
Gender: fx.Gender, Gender: fx.Gender,
StartDate: startDate, StartDate: startDate,
StatusType: fx.StatusType, StatusType: fx.StatusType,
StatusDescFK: strconv.Itoa(statusDescFK), StatusDescFK: fx.StatusDescFK,
RoundTypeFK: strconv.Itoa(roundTypeFK), RoundTypeFK: fx.RoundTypeFK,
UpdatesCount: updatesCount, UpdatesCount: updatesCount,
LastUpdatedAt: lastUpdatedAt, LastUpdatedAt: lastUpdated,
} }
dbFixture, err := s.store.CreateEnetpulseFixture(ctx, createFixture) // 5⃣ Save fixture using UPSERT repository method
if err != nil { if _, err := s.store.CreateEnetpulseFixture(ctx, fixture); err != nil {
fmt.Printf("failed storing fixture %s: %v\n", fx.FixtureID, err) fmt.Printf("failed upserting fixture %s: %v\n", fx.FixtureID, err)
continue continue
} }
allFixtures = append(allFixtures, dbFixture)
} }
fmt.Printf("✅ Successfully fetched and stored fixtures for sport %s\n", sport.SportID) fmt.Printf("✅ Successfully fetched and stored fixtures for sport %s\n", sport.SportID)
break // stop after first relevant sport break
} }
fmt.Println("✅ Completed fetching and storing fixtures for all sports") return nil
return allFixtures, nil
} }
// func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.EnetpulseFixture, error) {
// var allFixtures []domain.EnetpulseFixture
// // 1⃣ Fetch all sports from the database
// sports, err := s.store.GetAllEnetpulseSports(ctx)
// if err != nil {
// return nil, fmt.Errorf("failed to fetch sports from DB: %w", err)
// }
// // Struct for decoding each fixture from API
// type Fixture struct {
// FixtureID string `json:"id"`
// Name string `json:"name"`
// SportFK string `json:"sportFK"`
// TournamentFK string `json:"tournamentFK"`
// TournamentTemplateFK string `json:"tournament_templateFK"`
// TournamentStageFK string `json:"tournament_stageFK"`
// TournamentStageName string `json:"tournament_stage_name"`
// TournamentName string `json:"tournament_name"`
// TournamentTemplateName string `json:"tournament_template_name"`
// SportName string `json:"sport_name"`
// Gender string `json:"gender"`
// StartDate string `json:"startdate"`
// StatusType string `json:"status_type"`
// StatusDescFK string `json:"status_descFK"`
// RoundTypeFK string `json:"round_typeFK"`
// UpdatesCount string `json:"n"`
// LastUpdatedAt string `json:"ut"`
// }
// // 2⃣ Loop through each sport
// for _, sport := range sports {
// // Only fetch for sport "1" (Football)
// if sport.SportID != "1" {
// continue
// }
// url := fmt.Sprintf(
// "http://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s&sportFK=%s&language_typeFK=3&date=%s",
// s.cfg.EnetPulseConfig.UserName,
// s.cfg.EnetPulseConfig.Token,
// sport.SportID,
// date,
// )
// req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
// if err != nil {
// fmt.Printf("creating fixtures request for sport %s: %v\n", sport.SportID, err)
// continue
// }
// resp, err := s.httpClient.Do(req)
// if err != nil {
// fmt.Printf("requesting fixtures for sport %s: %v\n", sport.SportID, err)
// continue
// }
// defer resp.Body.Close()
// if resp.StatusCode != http.StatusOK {
// body, _ := io.ReadAll(resp.Body)
// fmt.Printf("failed to fetch fixtures for sport %s (status %d): %s\n",
// sport.SportID, resp.StatusCode, string(body))
// continue
// }
// // 3⃣ Decode API response
// var fixturesResp struct {
// Events map[string]Fixture `json:"events"`
// }
// if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
// fmt.Printf("decoding fixtures response for sport %s: %v\n", sport.SportID, err)
// continue
// }
// // 4⃣ Iterate over fixtures and store them
// for _, fx := range fixturesResp.Events {
// tournamentFK, _ := strconv.Atoi(fx.TournamentFK)
// tournamentTemplateFK, _ := strconv.Atoi(fx.TournamentTemplateFK)
// tournamentStageFK, _ := strconv.Atoi(fx.TournamentStageFK)
// statusDescFK, _ := strconv.Atoi(fx.StatusDescFK)
// roundTypeFK, _ := strconv.Atoi(fx.RoundTypeFK)
// updatesCount, _ := strconv.Atoi(fx.UpdatesCount)
// startDate, _ := time.Parse(time.RFC3339, fx.StartDate)
// lastUpdatedAt, _ := time.Parse(time.RFC3339, fx.LastUpdatedAt)
// createFixture := domain.CreateEnetpulseFixture{
// FixtureID: fx.FixtureID,
// Name: fx.Name,
// SportFK: fx.SportFK,
// TournamentFK: strconv.Itoa(tournamentFK),
// TournamentTemplateFK: strconv.Itoa(tournamentTemplateFK),
// TournamentStageFK: strconv.Itoa(tournamentStageFK),
// TournamentStageName: fx.TournamentStageName,
// TournamentName: fx.TournamentName,
// TournamentTemplateName: fx.TournamentTemplateName,
// SportName: fx.SportName,
// Gender: fx.Gender,
// StartDate: startDate,
// StatusType: fx.StatusType,
// StatusDescFK: strconv.Itoa(statusDescFK),
// RoundTypeFK: strconv.Itoa(roundTypeFK),
// UpdatesCount: updatesCount,
// LastUpdatedAt: lastUpdatedAt,
// }
// dbFixture, err := s.store.CreateEnetpulseFixture(ctx, createFixture)
// if err != nil {
// fmt.Printf("failed storing fixture %s: %v\n", fx.FixtureID, err)
// continue
// }
// allFixtures = append(allFixtures, dbFixture)
// }
// // fmt.Printf("✅ Successfully fetched and stored fixtures for sport %s\n", sport.SportID)
// break // stop after first relevant sport
// }
// // fmt.Println("✅ Completed fetching and storing fixtures for all sports")
// return allFixtures, nil
// }
func (s *Service) GetAllFixtures(ctx context.Context) ([]domain.EnetpulseFixture, error) { func (s *Service) GetAllFixtures(ctx context.Context) ([]domain.EnetpulseFixture, error) {
// 1⃣ Fetch all from store // 1⃣ Fetch all from store
fixtures, err := s.store.GetAllEnetpulseFixtures(ctx) fixtures, err := s.store.GetAllEnetpulseFixtures(ctx)
@ -955,136 +972,141 @@ func (s *Service) GetAllOutcomeTypes(ctx context.Context) ([]domain.EnetpulseOut
} }
func (s *Service) FetchAndStorePreodds(ctx context.Context) error { func (s *Service) FetchAndStorePreodds(ctx context.Context) error {
// 1⃣ Fetch all events from DB // 1⃣ Fetch all fixtures
fixtures, err := s.store.GetAllEnetpulseFixtures(ctx) fixtures, err := s.store.GetAllEnetpulseFixtures(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to fetch fixtures: %w", err) return fmt.Errorf("failed to fetch fixtures: %w", err)
} }
// providerIDStr := strconv.Itoa(int(s.cfg.EnetPulseConfig.ProviderID)) // 2⃣ Fetch all outcome types
outcomeTypes, err := s.store.GetAllEnetpulseOutcomeTypes(ctx)
if err != nil {
return fmt.Errorf("failed to fetch outcome types: %w", err)
}
// 2⃣ Loop through each fixture/event // 3⃣ Loop through each fixture
for _, fixture := range fixtures { for _, fixture := range fixtures {
url := fmt.Sprintf( // 4⃣ Loop through each outcome type
"http://eapi.enetpulse.com/preodds/event/?objectFK=%s&odds_providerFK=%s&username=%s&token=%s", for _, outcome := range outcomeTypes {
fixture.FixtureID, url := fmt.Sprintf(
s.cfg.EnetPulseConfig.ProviderID, "http://eapi.enetpulse.com/preodds/event/?objectFK=%s&odds_providerFK=%s&outcome_typeFK=%s&username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, fixture.FixtureID,
s.cfg.EnetPulseConfig.Token, s.cfg.EnetPulseConfig.ProviderID,
) outcome.OutcomeTypeID,
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
// optionally log error and continue to next fixture
continue
}
resp, err := s.httpClient.Do(req)
if err != nil {
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
continue
}
// Decode API response
var preoddsResp struct {
Preodds map[string]struct {
ID string `json:"id"`
OutcomeTypeFK string `json:"outcome_typeFK"`
OutcomeScopeFK string `json:"outcome_scopeFK"`
OutcomeSubtypeFK string `json:"outcome_subtypeFK"`
EventParticipantNumber string `json:"event_participant_number"`
Iparam string `json:"iparam"`
Iparam2 string `json:"iparam2"`
Dparam string `json:"dparam"`
Dparam2 string `json:"dparam2"`
Sparam string `json:"sparam"`
N string `json:"n"`
UT string `json:"ut"`
BettingOffers []struct {
ID string `json:"id"`
BettingOfferStatusFK int32 `json:"bettingoffer_status_fk"`
OddsProviderFK int32 `json:"odds_provider_fk"`
Odds float64 `json:"odds"`
OddsOld float64 `json:"odds_old"`
Active string `json:"active"`
CouponKey string `json:"coupon_key"`
N string `json:"n"`
UT string `json:"ut"`
} `json:"bettingoffers"`
} `json:"preodds"`
}
if err := json.NewDecoder(resp.Body).Decode(&preoddsResp); err != nil {
continue
}
// Iterate and store preodds and nested betting offers
for _, p := range preoddsResp.Preodds {
updatesCount := 0
if p.N != "" {
if n, err := strconv.Atoi(p.N); err == nil {
updatesCount = n
}
}
lastUpdatedAt, _ := time.Parse(time.RFC3339, p.UT)
eventParticipantNumber := int32(0)
if p.EventParticipantNumber != "" {
if epn, err := strconv.Atoi(p.EventParticipantNumber); err == nil {
eventParticipantNumber = int32(epn)
}
}
createPreodds := domain.CreateEnetpulsePreodds{
PreoddsID: p.ID,
EventFK: fixture.FixtureID,
OutcomeTypeFK: string(p.OutcomeTypeFK),
OutcomeScopeFK: string(p.OutcomeScopeFK),
OutcomeSubtypeFK: string(p.OutcomeSubtypeFK),
EventParticipantNumber: int(eventParticipantNumber),
IParam: p.Iparam,
IParam2: p.Iparam2,
DParam: p.Dparam,
DParam2: p.Dparam2,
SParam: p.Sparam,
UpdatesCount: int(updatesCount),
LastUpdatedAt: lastUpdatedAt,
}
storedPreodds, err := s.store.CreateEnetpulsePreodds(ctx, createPreodds)
if err != nil { if err != nil {
continue continue
} }
for _, o := range p.BettingOffers { resp, err := s.httpClient.Do(req)
bettingUpdates := 0 if err != nil {
if o.N != "" { continue
if n, err := strconv.Atoi(o.N); err == nil { }
bettingUpdates = n defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
continue
}
var preoddsResp struct {
Preodds map[string]struct {
ID string `json:"id"`
OutcomeTypeFK string `json:"outcome_typeFK"`
OutcomeScopeFK string `json:"outcome_scopeFK"`
OutcomeSubtypeFK string `json:"outcome_subtypeFK"`
EventParticipantNumber string `json:"event_participant_number"`
Iparam string `json:"iparam"`
Iparam2 string `json:"iparam2"`
Dparam string `json:"dparam"`
Dparam2 string `json:"dparam2"`
Sparam string `json:"sparam"`
N string `json:"n"`
UT string `json:"ut"`
BettingOffers []struct {
ID string `json:"id"`
BettingOfferStatusFK int32 `json:"bettingoffer_status_fk"`
OddsProviderFK int32 `json:"odds_provider_fk"`
Odds float64 `json:"odds"`
OddsOld float64 `json:"odds_old"`
Active string `json:"active"`
CouponKey string `json:"coupon_key"`
N string `json:"n"`
UT string `json:"ut"`
} `json:"bettingoffers"`
} `json:"preodds"`
}
if err := json.NewDecoder(resp.Body).Decode(&preoddsResp); err != nil {
continue
}
for _, p := range preoddsResp.Preodds {
updatesCount := 0
if p.N != "" {
if n, err := strconv.Atoi(p.N); err == nil {
updatesCount = n
} }
} }
bettingLastUpdatedAt, _ := time.Parse(time.RFC3339, o.UT) lastUpdatedAt, _ := time.Parse(time.RFC3339, p.UT)
createOffer := domain.CreateEnetpulsePreoddsBettingOffer{ eventParticipantNumber := int32(0)
BettingOfferID: o.ID, if p.EventParticipantNumber != "" {
PreoddsFK: storedPreodds.PreoddsID, if epn, err := strconv.Atoi(p.EventParticipantNumber); err == nil {
BettingOfferStatusFK: o.BettingOfferStatusFK, eventParticipantNumber = int32(epn)
OddsProviderFK: o.OddsProviderFK, }
Odds: o.Odds,
OddsOld: o.OddsOld,
Active: o.Active,
CouponKey: o.CouponKey,
UpdatesCount: int(bettingUpdates),
LastUpdatedAt: bettingLastUpdatedAt,
} }
_, _ = s.store.CreateEnetpulsePreoddsBettingOffer(ctx, createOffer) createPreodds := domain.CreateEnetpulsePreodds{
PreoddsID: p.ID,
EventFK: fixture.FixtureID,
OutcomeTypeFK: outcome.OutcomeTypeID,
OutcomeScopeFK: string(p.OutcomeScopeFK),
OutcomeSubtypeFK: string(p.OutcomeSubtypeFK),
EventParticipantNumber: int(eventParticipantNumber),
IParam: p.Iparam,
IParam2: p.Iparam2,
DParam: p.Dparam,
DParam2: p.Dparam2,
SParam: p.Sparam,
UpdatesCount: int(updatesCount),
LastUpdatedAt: lastUpdatedAt,
}
storedPreodds, err := s.store.CreateEnetpulsePreodds(ctx, createPreodds)
if err != nil {
continue
}
for _, o := range p.BettingOffers {
bettingUpdates := 0
if o.N != "" {
if n, err := strconv.Atoi(o.N); err == nil {
bettingUpdates = n
}
}
bettingLastUpdatedAt, _ := time.Parse(time.RFC3339, o.UT)
createOffer := domain.CreateEnetpulsePreoddsBettingOffer{
BettingOfferID: o.ID,
PreoddsFK: storedPreodds.PreoddsID,
BettingOfferStatusFK: o.BettingOfferStatusFK,
OddsProviderFK: o.OddsProviderFK,
Odds: o.Odds,
OddsOld: o.OddsOld,
Active: o.Active,
CouponKey: o.CouponKey,
UpdatesCount: int(bettingUpdates),
LastUpdatedAt: bettingLastUpdatedAt,
}
_, _ = s.store.CreateEnetpulsePreoddsBettingOffer(ctx, createOffer)
}
} }
} }
} }
@ -1208,6 +1230,16 @@ func (s *Service) GetAllBettingOffers(ctx context.Context) ([]domain.EnetpulsePr
return offers, nil return offers, nil
} }
func (s *Service) GetFixturesWithPreodds(ctx context.Context) ([]domain.EnetpulseFixtureWithPreodds, error) {
// 1⃣ Fetch fixtures and their associated preodds from the repository
fixtures, err := s.store.GetFixturesWithPreodds(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch fixtures with preodds from DB: %w", err)
}
return fixtures, nil
}
// helper to safely parse string to int32 // helper to safely parse string to int32
// func parseStringToInt32(s string) int32 { // func parseStringToInt32(s string) int32 {
// if s == "" { // if s == "" {

View File

@ -18,6 +18,7 @@ type Service interface {
UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) error UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) error
GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error)
GetEventWithSettingByID(ctx context.Context, ID int64, companyID int64) (domain.EventWithSettings, error) GetEventWithSettingByID(ctx context.Context, ID int64, companyID int64) (domain.EventWithSettings, error)
UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error
UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error
GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error)
} }

View File

@ -491,8 +491,11 @@ func (s *service) GetEventWithSettingByID(ctx context.Context, ID int64, company
return s.store.GetEventWithSettingByID(ctx, ID, companyID) return s.store.GetEventWithSettingByID(ctx, ID, companyID)
} }
func (s *service) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error { func (s *service) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error {
return s.store.UpdateEventSettings(ctx, event) return s.store.UpdateTenantEventSettings(ctx, event)
}
func (s *service) UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error {
return s.store.UpdateGlobalEventSettings(ctx, event)
} }
func (s *service) GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) { func (s *service) GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) {

View File

@ -1,67 +1,67 @@
package kafka package kafka
import ( // import (
"context" // "context"
"encoding/json" // "encoding/json"
"log" // "log"
"github.com/SamuelTariku/FortuneBet-Backend/internal/event" // "github.com/SamuelTariku/FortuneBet-Backend/internal/event"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws" // "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws"
"github.com/segmentio/kafka-go" // "github.com/segmentio/kafka-go"
) // )
type WalletConsumer struct { // type WalletConsumer struct {
reader *kafka.Reader // reader *kafka.Reader
hub *ws.NotificationHub // hub *ws.NotificationHub
topic string // topic string
groupID string // groupID string
brokers []string // brokers []string
} // }
func NewWalletConsumer(brokers []string, topic, groupID string, hub *ws.NotificationHub) *WalletConsumer { // func NewWalletConsumer(brokers []string, topic, groupID string, hub *ws.NotificationHub) *WalletConsumer {
return &WalletConsumer{ // return &WalletConsumer{
brokers: brokers, // brokers: brokers,
topic: topic, // topic: topic,
groupID: groupID, // groupID: groupID,
hub: hub, // hub: hub,
reader: kafka.NewReader(kafka.ReaderConfig{ // reader: kafka.NewReader(kafka.ReaderConfig{
Brokers: brokers, // Brokers: brokers,
GroupID: groupID, // GroupID: groupID,
Topic: topic, // Topic: topic,
}), // }),
} // }
} // }
func (c *WalletConsumer) Start(ctx context.Context) { // func (c *WalletConsumer) Start(ctx context.Context) {
go func() { // go func() {
for { // for {
m, err := c.reader.ReadMessage(ctx) // m, err := c.reader.ReadMessage(ctx)
if err != nil { // if err != nil {
log.Printf("Error reading wallet Kafka message: %v", err) // log.Printf("Error reading wallet Kafka message: %v", err)
continue // continue
} // }
var evt event.WalletEvent // var evt event.WalletEvent
if err := json.Unmarshal(m.Value, &evt); err != nil { // if err := json.Unmarshal(m.Value, &evt); err != nil {
log.Printf("Failed to unmarshal wallet event: %v", err) // log.Printf("Failed to unmarshal wallet event: %v", err)
continue // continue
} // }
payload := map[string]interface{}{ // payload := map[string]interface{}{
"type": evt.EventType, // "type": evt.EventType,
"wallet_id": evt.WalletID, // "wallet_id": evt.WalletID,
"user_id": evt.UserID, // "user_id": evt.UserID,
"balance": evt.Balance, // "balance": evt.Balance,
"wallet_type": evt.WalletType, // "wallet_type": evt.WalletType,
"trigger": evt.Trigger, // "trigger": evt.Trigger,
"recipient_id": evt.UserID, // "recipient_id": evt.UserID,
} // }
// Broadcast to appropriate WebSocket clients // // Broadcast to appropriate WebSocket clients
c.hub.Broadcast <- payload // c.hub.Broadcast <- payload
} // }
}() // }()
} // }
// func (c *WalletConsumer) Shutdown() error { // func (c *WalletConsumer) Shutdown() error {
// return c.reader.Close() // return c.reader.Close()

View File

@ -1,36 +1,36 @@
package kafka package kafka
import ( // import (
"context" // "context"
"encoding/json" // "encoding/json"
"time" // "time"
"github.com/segmentio/kafka-go" // "github.com/segmentio/kafka-go"
) // )
type Producer struct { // type Producer struct {
writer *kafka.Writer // writer *kafka.Writer
} // }
func NewProducer(brokers []string, topic string) *Producer { // func NewProducer(brokers []string, topic string) *Producer {
return &Producer{ // return &Producer{
writer: &kafka.Writer{ // writer: &kafka.Writer{
Addr: kafka.TCP(brokers...), // Addr: kafka.TCP(brokers...),
Topic: topic, // Topic: topic,
Balancer: &kafka.LeastBytes{}, // Balancer: &kafka.LeastBytes{},
}, // },
} // }
} // }
func (p *Producer) Publish(ctx context.Context, key string, event any) error { // func (p *Producer) Publish(ctx context.Context, key string, event any) error {
msgBytes, err := json.Marshal(event) // msgBytes, err := json.Marshal(event)
if err != nil { // if err != nil {
return err // return err
} // }
return p.writer.WriteMessages(ctx, kafka.Message{ // return p.writer.WriteMessages(ctx, kafka.Message{
Key: []byte(key), // Key: []byte(key),
Value: msgBytes, // Value: msgBytes,
Time: time.Now(), // Time: time.Now(),
}) // })
} // }

View File

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

View File

@ -25,7 +25,7 @@ func (s *service) SaveLeagueSettings(ctx context.Context, leagueSettings domain.
return s.store.SaveLeagueSettings(ctx, leagueSettings) return s.store.SaveLeagueSettings(ctx, leagueSettings)
} }
func (s *service) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error) { func (s *service) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, int64, error) {
return s.store.GetAllLeagues(ctx, filter) return s.store.GetAllLeagues(ctx, filter)
} }
@ -40,3 +40,7 @@ func (s *service) CheckLeagueSupport(ctx context.Context, leagueID int64, compan
func (s *service) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error { func (s *service) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {
return s.store.UpdateLeague(ctx, league) return s.store.UpdateLeague(ctx, league)
} }
func (s *service) UpdateGlobalLeagueSettings(ctx context.Context, league domain.UpdateGlobalLeagueSettings) error {
return s.store.UpdateGlobalLeagueSettings(ctx, league)
}

View File

@ -2,7 +2,6 @@ package notificationservice
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
// "errors" // "errors"
@ -12,19 +11,19 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/config"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/event"
"github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers" "github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers"
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository" "github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/segmentio/kafka-go" // "github.com/segmentio/kafka-go"
"go.uber.org/zap" "go.uber.org/zap"
// "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" // "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws"
// afro "github.com/amanuelabay/afrosms-go" // afro "github.com/amanuelabay/afrosms-go"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/redis/go-redis/v9" // "github.com/redis/go-redis/v9"
) )
type Service struct { type Service struct {
@ -39,8 +38,6 @@ type Service struct {
messengerSvc *messenger.Service messengerSvc *messenger.Service
mongoLogger *zap.Logger mongoLogger *zap.Logger
logger *slog.Logger logger *slog.Logger
redisClient *redis.Client
reader *kafka.Reader
} }
func New(repo repository.NotificationRepository, func New(repo repository.NotificationRepository,
@ -49,17 +46,8 @@ func New(repo repository.NotificationRepository,
cfg *config.Config, cfg *config.Config,
messengerSvc *messenger.Service, messengerSvc *messenger.Service,
userSvc *user.Service, userSvc *user.Service,
kafkaBrokers []string,
) *Service { ) *Service {
hub := ws.NewNotificationHub() hub := ws.NewNotificationHub()
rdb := redis.NewClient(&redis.Options{
Addr: cfg.RedisAddr, // e.g., "redis:6379"
})
walletReader := kafka.NewReader(kafka.ReaderConfig{
Brokers: kafkaBrokers,
Topic: "wallet-balance-topic",
GroupID: "notification-service-group", // Each service should have its own group
})
svc := &Service{ svc := &Service{
repo: repo, repo: repo,
@ -72,15 +60,13 @@ func New(repo repository.NotificationRepository,
messengerSvc: messengerSvc, messengerSvc: messengerSvc,
userSvc: userSvc, userSvc: userSvc,
config: cfg, config: cfg,
redisClient: rdb,
reader: walletReader,
} }
go hub.Run() go hub.Run()
go svc.startWorker() go svc.startWorker()
go svc.startRetryWorker() go svc.startRetryWorker()
go svc.RunRedisSubscriber(context.Background()) // go svc.RunRedisSubscriber(context.Background())
go svc.StartKafkaConsumer(context.Background()) // go svc.StartKafkaConsumer(context.Background())
return svc return svc
} }
@ -484,189 +470,192 @@ func (s *Service) CountUnreadNotifications(ctx context.Context, recipient_id int
return s.repo.CountUnreadNotifications(ctx, recipient_id) return s.repo.CountUnreadNotifications(ctx, recipient_id)
} }
func (s *Service) DeleteOldNotifications(ctx context.Context) error {
return s.repo.DeleteOldNotifications(ctx)
}
// func (s *Service) GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error){ // func (s *Service) GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error){
// return s.repo.Get(ctx, filter) // return s.repo.Get(ctx, filter)
// } // }
func (s *Service) RunRedisSubscriber(ctx context.Context) { // func (s *Service) RunRedisSubscriber(ctx context.Context) {
pubsub := s.redisClient.Subscribe(ctx, "live_metrics") // pubsub := s.redisClient.Subscribe(ctx, "live_metrics")
defer pubsub.Close() // defer pubsub.Close()
ch := pubsub.Channel() // ch := pubsub.Channel()
for msg := range ch { // for msg := range ch {
var parsed map[string]interface{} // var parsed map[string]interface{}
if err := json.Unmarshal([]byte(msg.Payload), &parsed); err != nil { // if err := json.Unmarshal([]byte(msg.Payload), &parsed); err != nil {
// s.logger.Error("invalid Redis message format", "payload", msg.Payload, "error", err) // // s.logger.Error("invalid Redis message format", "payload", msg.Payload, "error", err)
s.mongoLogger.Error("invalid Redis message format", // s.mongoLogger.Error("invalid Redis message format",
zap.String("payload", msg.Payload), // zap.String("payload", msg.Payload),
zap.Error(err), // zap.Error(err),
zap.Time("timestamp", time.Now()), // zap.Time("timestamp", time.Now()),
) // )
continue // continue
} // }
eventType, _ := parsed["type"].(string) // eventType, _ := parsed["type"].(string)
payload := parsed["payload"] // payload := parsed["payload"]
recipientID, hasRecipient := parsed["recipient_id"] // recipientID, hasRecipient := parsed["recipient_id"]
recipientType, _ := parsed["recipient_type"].(string) // recipientType, _ := parsed["recipient_type"].(string)
message := map[string]interface{}{ // message := map[string]interface{}{
"type": eventType, // "type": eventType,
"payload": payload, // "payload": payload,
} // }
if hasRecipient { // if hasRecipient {
message["recipient_id"] = recipientID // message["recipient_id"] = recipientID
message["recipient_type"] = recipientType // message["recipient_type"] = recipientType
} // }
s.Hub.Broadcast <- message // s.Hub.Broadcast <- message
} // }
} // }
func (s *Service) UpdateLiveWalletMetrics(ctx context.Context, companies []domain.GetCompany, branches []domain.BranchWallet) error { // func (s *Service) UpdateLiveWalletMetrics(ctx context.Context, companies []domain.GetCompany, branches []domain.BranchWallet) error {
const key = "live_metrics" // const key = "live_metrics"
companyBalances := make([]domain.CompanyWalletBalance, 0, len(companies)) // companyBalances := make([]domain.CompanyWalletBalance, 0, len(companies))
for _, c := range companies { // for _, c := range companies {
companyBalances = append(companyBalances, domain.CompanyWalletBalance{ // companyBalances = append(companyBalances, domain.CompanyWalletBalance{
CompanyID: c.ID, // CompanyID: c.ID,
CompanyName: c.Name, // CompanyName: c.Name,
Balance: float64(c.WalletBalance.Float32()), // Balance: float64(c.WalletBalance.Float32()),
}) // })
} // }
branchBalances := make([]domain.BranchWalletBalance, 0, len(branches)) // branchBalances := make([]domain.BranchWalletBalance, 0, len(branches))
for _, b := range branches { // for _, b := range branches {
branchBalances = append(branchBalances, domain.BranchWalletBalance{ // branchBalances = append(branchBalances, domain.BranchWalletBalance{
BranchID: b.ID, // BranchID: b.ID,
BranchName: b.Name, // BranchName: b.Name,
CompanyID: b.CompanyID, // CompanyID: b.CompanyID,
Balance: float64(b.Balance.Float32()), // Balance: float64(b.Balance.Float32()),
}) // })
} // }
payload := domain.LiveWalletMetrics{ // payload := domain.LiveWalletMetrics{
Timestamp: time.Now(), // Timestamp: time.Now(),
CompanyBalances: companyBalances, // CompanyBalances: companyBalances,
BranchBalances: branchBalances, // BranchBalances: branchBalances,
} // }
updatedData, err := json.Marshal(payload) // updatedData, err := json.Marshal(payload)
if err != nil { // if err != nil {
return err // return err
} // }
if err := s.redisClient.Set(ctx, key, updatedData, 0).Err(); err != nil { // if err := s.redisClient.Set(ctx, key, updatedData, 0).Err(); err != nil {
return err // return err
} // }
if err := s.redisClient.Publish(ctx, key, updatedData).Err(); err != nil { // if err := s.redisClient.Publish(ctx, key, updatedData).Err(); err != nil {
return err // return err
} // }
return nil // return nil
} // }
func (s *Service) GetLiveMetrics(ctx context.Context) (domain.LiveMetric, error) { // func (s *Service) GetLiveMetrics(ctx context.Context) (domain.LiveMetric, error) {
const key = "live_metrics" // const key = "live_metrics"
var metric domain.LiveMetric // var metric domain.LiveMetric
val, err := s.redisClient.Get(ctx, key).Result() // val, err := s.redisClient.Get(ctx, key).Result()
if err == redis.Nil { // if err == redis.Nil {
// Key does not exist yet, return zero-valued struct // // Key does not exist yet, return zero-valued struct
return domain.LiveMetric{}, nil // return domain.LiveMetric{}, nil
} else if err != nil { // } else if err != nil {
return domain.LiveMetric{}, err // return domain.LiveMetric{}, err
} // }
if err := json.Unmarshal([]byte(val), &metric); err != nil { // if err := json.Unmarshal([]byte(val), &metric); err != nil {
return domain.LiveMetric{}, err // return domain.LiveMetric{}, err
} // }
return metric, nil // return metric, nil
} // }
// func (s *Service) StartKafkaConsumer(ctx context.Context) {
// go func() {
// for {
// m, err := s.reader.ReadMessage(ctx)
// if err != nil {
// if err == context.Canceled {
// s.mongoLogger.Info("[NotificationSvc.KafkaConsumer] Stopped by context")
// return
// }
// s.mongoLogger.Error("[NotificationSvc.KafkaConsumer] Error reading message",
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// time.Sleep(1 * time.Second) // backoff
// continue
// }
func (s *Service) StartKafkaConsumer(ctx context.Context) { // var walletEvent event.WalletEvent
go func() { // if err := json.Unmarshal(m.Value, &walletEvent); err != nil {
for { // s.mongoLogger.Error("[NotificationSvc.KafkaConsumer] Failed to unmarshal wallet event",
m, err := s.reader.ReadMessage(ctx) // zap.String("message", string(m.Value)),
if err != nil { // zap.Error(err),
if err == context.Canceled { // zap.Time("timestamp", time.Now()),
s.mongoLogger.Info("[NotificationSvc.KafkaConsumer] Stopped by context") // )
return // continue
} // }
s.mongoLogger.Error("[NotificationSvc.KafkaConsumer] Error reading message",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
time.Sleep(1 * time.Second) // backoff
continue
}
var walletEvent event.WalletEvent // raw, _ := json.Marshal(map[string]any{
if err := json.Unmarshal(m.Value, &walletEvent); err != nil { // "balance": walletEvent.Balance.Float32(),
s.mongoLogger.Error("[NotificationSvc.KafkaConsumer] Failed to unmarshal wallet event", // "type": walletEvent.WalletType,
zap.String("message", string(m.Value)), // "timestamp": time.Now(),
zap.Error(err), // })
zap.Time("timestamp", time.Now()),
)
continue
}
raw, _ := json.Marshal(map[string]any{ // headline := ""
"balance": walletEvent.Balance.Float32(), // message := ""
"type": walletEvent.WalletType, // var receiver domain.NotificationRecieverSide
"timestamp": time.Now(), // switch walletEvent.WalletType {
})
headline := "" // case domain.StaticWalletType:
message := "" // headline = "Referral and Bonus Wallet Updated"
var receiver domain.NotificationRecieverSide // message = fmt.Sprintf("Your referral and bonus wallet balance is now %.2f", walletEvent.Balance.Float32())
switch walletEvent.WalletType { // receiver = domain.NotificationRecieverSideCustomer
// case domain.RegularWalletType:
// headline = "Wallet Updated"
// message = fmt.Sprintf("Your wallet balance is now %.2f", walletEvent.Balance.Float32())
// receiver = domain.NotificationRecieverSideCustomer
// case domain.BranchWalletType:
// headline = "Branch Wallet Updated"
// message = fmt.Sprintf("branch wallet balance is now %.2f", walletEvent.Balance.Float32())
// receiver = domain.NotificationRecieverSideBranchManager
// case domain.CompanyWalletType:
// headline = "Company Wallet Updated"
// message = fmt.Sprintf("company wallet balance is now %.2f", walletEvent.Balance.Float32())
// receiver = domain.NotificationRecieverSideAdmin
// }
// // Handle the wallet event: send notification
// notification := &domain.Notification{
// RecipientID: walletEvent.UserID,
// DeliveryChannel: domain.DeliveryChannelInApp,
// Reciever: receiver,
// Type: domain.NotificationTypeWalletUpdated,
// DeliveryStatus: domain.DeliveryStatusPending,
// IsRead: false,
// Level: domain.NotificationLevelInfo,
// Priority: 2,
// Metadata: raw,
// Payload: domain.NotificationPayload{
// Headline: headline,
// Message: message,
// },
// }
case domain.StaticWalletType: // if err := s.SendNotification(ctx, notification); err != nil {
headline = "Referral and Bonus Wallet Updated" // s.mongoLogger.Error("[NotificationSvc.KafkaConsumer] Failed to send notification",
message = fmt.Sprintf("Your referral and bonus wallet balance is now %.2f", walletEvent.Balance.Float32()) // zap.Error(err),
receiver = domain.NotificationRecieverSideCustomer // zap.Time("timestamp", time.Now()),
case domain.RegularWalletType: // )
headline = "Wallet Updated" // }
message = fmt.Sprintf("Your wallet balance is now %.2f", walletEvent.Balance.Float32()) // }
receiver = domain.NotificationRecieverSideCustomer // }()
case domain.BranchWalletType: // }
headline = "Branch Wallet Updated"
message = fmt.Sprintf("branch wallet balance is now %.2f", walletEvent.Balance.Float32())
receiver = domain.NotificationRecieverSideBranchManager
case domain.CompanyWalletType:
headline = "Company Wallet Updated"
message = fmt.Sprintf("company wallet balance is now %.2f", walletEvent.Balance.Float32())
receiver = domain.NotificationRecieverSideAdmin
}
// Handle the wallet event: send notification
notification := &domain.Notification{
RecipientID: walletEvent.UserID,
DeliveryChannel: domain.DeliveryChannelInApp,
Reciever: receiver,
Type: domain.NotificationTypeWalletUpdated,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Level: domain.NotificationLevelInfo,
Priority: 2,
Metadata: raw,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
}
if err := s.SendNotification(ctx, notification); err != nil {
s.mongoLogger.Error("[NotificationSvc.KafkaConsumer] Failed to send notification",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
}
}
}()
}
// func (s *Service) UpdateLiveWalletMetricForWallet(ctx context.Context, wallet domain.Wallet) { // func (s *Service) UpdateLiveWalletMetricForWallet(ctx context.Context, wallet domain.Wallet) {
// var ( // var (

View File

@ -17,31 +17,17 @@ type Service interface {
GetALLPrematchOdds(ctx context.Context) ([]domain.OddMarket, error) GetALLPrematchOdds(ctx context.Context) ([]domain.OddMarket, error)
// GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.OddMarket, error) // GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.OddMarket, error)
DeleteOddsForEvent(ctx context.Context, eventID string) error DeleteOddsForEvent(ctx context.Context, eventID string) error
GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error) GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error)
GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error)
// Settings // Settings
SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error
UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error
DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error
DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error
// Odd History // Odd History
InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error) InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error)
GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error)
GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error)
// Disabling Odds
InsertDisabledOdd(ctx context.Context, odd domain.CreateDisabledOdd) (domain.DisabledOdd, error)
GetAllDisabledOdds(ctx context.Context) ([]domain.DisabledOdd, error)
GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (domain.DisabledOdd, error)
GetDisabledOddByID(ctx context.Context, id int64) (domain.DisabledOdd, error)
DeleteDisabledOddsByID(ctx context.Context, id int64) error
DeleteDisabledOddsByRawOddID(ctx context.Context, id int64) error
// Custom Odds
// InsertCustomOdds(ctx context.Context, odd domain.CreateCustomOdd) (domain.CustomOdd, error)
// GetAllCustomOdds(ctx context.Context, filter domain.CustomOddFilter) ([]domain.CustomOdd, error)
// GetCustomOddByID(ctx context.Context, id int64) (domain.CustomOdd, error)
// GetCustomOddByOddID(ctx context.Context, oddId int64, companyID int64) (domain.CustomOdd, error)
// DeleteCustomOddByID(ctx context.Context, id int64) error
// DeleteCustomOddsByOddID(ctx context.Context, oddId int64, companyID int64) error
// DeleteCustomOddByEventID(ctx context.Context, eventID string) error
} }

View File

@ -557,13 +557,14 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID int64, fi, secti
} }
marketRecord := domain.CreateOddMarket{ marketRecord := domain.CreateOddMarket{
EventID: eventID, EventID: eventID,
MarketCategory: sectionName, MarketCategory: sectionName,
MarketType: marketType, MarketType: marketType,
MarketName: market.Name, MarketName: market.Name,
MarketID: marketIDint, MarketID: marketIDint,
UpdatedAt: updatedAt, NumberOfOutcomes: int64(len(market.Odds)),
Odds: marketOdds, UpdatedAt: updatedAt,
Odds: marketOdds,
// bwin won't reach this code so bet365 is hardcoded for now // bwin won't reach this code so bet365 is hardcoded for now
} }
@ -676,6 +677,10 @@ func (s *ServiceImpl) SaveOddsSetting(ctx context.Context, odd domain.CreateOddM
return s.store.SaveOddsSetting(ctx, odd) return s.store.SaveOddsSetting(ctx, odd)
} }
func (s *ServiceImpl) UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error {
return s.store.UpdateGlobalOddsSetting(ctx, odd)
}
func (s *ServiceImpl) SaveOddsSettingReq(ctx context.Context, companyID int64, req domain.CreateOddMarketSettingsReq) error { func (s *ServiceImpl) SaveOddsSettingReq(ctx context.Context, companyID int64, req domain.CreateOddMarketSettingsReq) error {
odd, err := s.GetOddsWithSettingsByID(ctx, req.OddMarketID, companyID) odd, err := s.GetOddsWithSettingsByID(ctx, req.OddMarketID, companyID)
@ -741,6 +746,14 @@ func (s *ServiceImpl) DeleteOddsForEvent(ctx context.Context, eventID int64) err
return s.store.DeleteOddsForEvent(ctx, eventID) return s.store.DeleteOddsForEvent(ctx, eventID)
} }
func (s *ServiceImpl) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error {
return s.store.DeleteAllCompanyOddsSetting(ctx, companyID)
}
func (s *ServiceImpl) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error {
return s.store.DeleteCompanyOddsSettingByOddMarketID(ctx, companyID, oddMarketID)
}
// func getString(v interface{}) string { // func getString(v interface{}) string {
// if str, ok := v.(string); ok { // if str, ok := v.(string); ok {
// return str // return str

View File

@ -16,9 +16,12 @@ type RaffleStore interface {
CreateRaffleWinner(ctx context.Context, raffleWinnerParams domain.RaffleWinnerParams) error CreateRaffleWinner(ctx context.Context, raffleWinnerParams domain.RaffleWinnerParams) error
SetRaffleComplete(ctx context.Context, raffleID int32) error SetRaffleComplete(ctx context.Context, raffleID int32) error
CheckValidSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) (bool, error) CheckValidSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) (bool, error)
CheckSportRaffleHasFilter(ctx context.Context, raffleID int32) (bool, error)
CreateRaffleTicket(ctx context.Context, raffleTicketParams domain.CreateRaffleTicket) (domain.RaffleTicket, error) CreateRaffleTicket(ctx context.Context, raffleTicketParams domain.CreateRaffleTicket) (domain.RaffleTicket, error)
GetUserRaffleTickets(ctx context.Context, userID int32) ([]domain.RaffleTicketRes, error) GetUserRaffleTickets(ctx context.Context, userID int32) ([]domain.RaffleTicketRes, error)
SuspendRaffleTicket(ctx context.Context, raffleTicketID int32) error SuspendRaffleTicket(ctx context.Context, raffleTicketID int32) error
UnSuspendRaffleTicket(ctx context.Context, raffleID int32) error UnSuspendRaffleTicket(ctx context.Context, raffleID int32) error
GetRaffleTicketCount(ctx context.Context, raffleID, userID int32) (int64, error)
GetRaffleTicketLimit(ctx context.Context, raffleID int32) (int32, error)
} }

View File

@ -64,3 +64,15 @@ func (s *Service) UnSuspendRaffleTicket(ctx context.Context, raffleID int32) err
func (s *Service) CheckValidSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) (bool, error) { func (s *Service) CheckValidSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) (bool, error) {
return s.raffleStore.CheckValidSportRaffleFilter(ctx, raffleID, sportID, leagueID) return s.raffleStore.CheckValidSportRaffleFilter(ctx, raffleID, sportID, leagueID)
} }
func (s *Service) CheckSportRaffleHasFilter(ctx context.Context, raffleID int32) (bool, error) {
return s.raffleStore.CheckSportRaffleHasFilter(ctx, raffleID)
}
func (s *Service) GetRaffleTicketCount(ctx context.Context, raffleID, userID int32) (int64, error) {
return s.raffleStore.GetRaffleTicketCount(ctx, raffleID, userID)
}
func (s *Service) GetRaffleTicketLimit(ctx context.Context, raffleID int32) (int32, error) {
return s.raffleStore.GetRaffleTicketLimit(ctx, raffleID)
}

View File

@ -135,11 +135,11 @@ func (s *Service) GetDashboardSummary(ctx context.Context, filter domain.ReportF
} }
// Get sport/game metrics // Get sport/game metrics
summary.TotalGames, summary.ActiveGames, summary.InactiveGames, err = s.virtulaGamesStore.GetGameCounts(ctx, filter) // summary.TotalGames, summary.ActiveGames, summary.InactiveGames, err = s.virtulaGamesStore.GetGameCounts(ctx, filter)
if err != nil { // if err != nil {
s.logger.Error("failed to get game counts", "error", err) // s.logger.Error("failed to get game counts", "error", err)
return domain.DashboardSummary{}, err // return domain.DashboardSummary{}, err
} // }
// Get company metrics // Get company metrics
summary.TotalCompanies, summary.ActiveCompanies, summary.InactiveCompanies, err = s.companyStore.GetCompanyCounts(ctx, filter) summary.TotalCompanies, summary.ActiveCompanies, summary.InactiveCompanies, err = s.companyStore.GetCompanyCounts(ctx, filter)

View File

@ -0,0 +1,292 @@
package result
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"go.uber.org/zap"
)
func (s *Service) CheckAndSendResultNotifications(ctx context.Context, createdAfter time.Time) error {
resultLog, err := s.repo.GetAllResultLog(ctx, domain.ResultLogFilter{
CreatedAfter: domain.ValidTime{
Value: createdAfter,
Valid: true,
},
})
if err != nil {
s.mongoLogger.Error(
"Failed to get result log",
zap.Time("CreatedAfter", createdAfter),
zap.Error(err),
)
return err
}
if len(resultLog) == 0 {
s.mongoLogger.Info(
"No results found for check and send result notification",
zap.Time("CreatedAfter", createdAfter),
)
return nil
}
totalResultLog := domain.ResultLog{
StatusNotFinishedCount: resultLog[0].StatusNotFinishedCount,
StatusPostponedCount: resultLog[0].StatusPostponedCount,
}
for _, log := range resultLog {
// Add all the bets
totalResultLog.StatusNotFinishedBets += log.StatusNotFinishedBets
totalResultLog.StatusPostponedBets += log.StatusPostponedBets
totalResultLog.StatusToBeFixedBets += log.StatusToBeFixedBets
totalResultLog.StatusRemovedBets += log.StatusRemovedBets
totalResultLog.StatusEndedBets += log.StatusEndedBets
totalResultLog.StatusToBeFixedCount += log.StatusToBeFixedCount
totalResultLog.StatusRemovedCount += log.StatusRemovedCount
totalResultLog.StatusEndedCount += log.StatusEndedCount
totalResultLog.RemovedCount += log.RemovedCount
}
err = s.SendAdminResultStatusErrorNotification(ctx, totalResultLog, createdAfter, time.Now())
if err != nil {
s.mongoLogger.Error(
"Failed to send admin result status notification",
zap.Time("CreatedAfter", createdAfter),
zap.Error(err),
)
return err
}
return nil
}
func buildHeadlineAndMessage(counts domain.ResultLog, createdAfter time.Time, endTime time.Time) (string, string) {
period := fmt.Sprintf("%s - %s", createdAfter.Format("02 Jan 2006"), endTime.Format("02 Jan 2006"))
totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount
totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets
if totalIssues == 0 {
return "✅ Successfully Processed Event Results", fmt.Sprintf(
"%d total ended events with %d total bets. No issues detected", counts.StatusEndedCount, totalBets,
)
}
parts := []string{}
if counts.StatusNotFinishedCount > 0 {
parts = append(parts, fmt.Sprintf("%d unfinished with %d bets", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
}
if counts.StatusToBeFixedCount > 0 {
parts = append(parts, fmt.Sprintf("%d to-fix with %d bets", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
}
if counts.StatusPostponedCount > 0 {
parts = append(parts, fmt.Sprintf("%d postponed with %d bets", counts.StatusPostponedCount, counts.StatusPostponedBets))
}
if counts.StatusRemovedCount > 0 {
parts = append(parts, fmt.Sprintf("%d removed with %d bets", counts.StatusRemovedCount, counts.StatusRemovedBets))
}
if counts.StatusEndedCount > 0 {
parts = append(parts, fmt.Sprintf("%d ended with %d bets", counts.StatusEndedCount, counts.StatusEndedBets))
}
headline := "⚠️ Issues Found Processing Event Results"
message := fmt.Sprintf("Processed expired event results (%s): %s. Please review pending entries.",
period, strings.Join(parts, ", "))
return headline, message
}
func buildHeadlineAndMessageEmail(counts domain.ResultLog, user domain.User, createdAfter time.Time, endTime time.Time) (string, string, string) {
period := fmt.Sprintf("%s - %s", createdAfter.Format("02 Jan 2006"), endTime.Format("02 Jan 2006"))
totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount +
counts.StatusPostponedCount + counts.StatusRemovedCount
totalEvents := counts.StatusEndedCount + counts.StatusNotFinishedCount +
counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount
totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets +
counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets
greeting := fmt.Sprintf("Hi %s %s,", user.FirstName, user.LastName)
if totalIssues == 0 {
headline := "✅ Weekly Results Report — All Events Processed Successfully"
plain := fmt.Sprintf(`%s
Weekly Results Summary (%s):
- %d Ended Events
- %d Total Bets
All events were processed successfully, and no issues were detected.
Best regards,
The System`, greeting, period, counts.StatusEndedCount, totalBets)
html := fmt.Sprintf(`<p>%s</p>
<h2>Weekly Results Summary</h2>
<p><em>Period: %s</em></p>
<ul>
<li><strong>%d Ended Events</strong></li>
<li><strong>%d Total Bets</strong></li>
</ul>
<p>All events were processed successfully, and no issues were detected.</p>
<p>Best regards,<br>The System</p>`,
greeting, period, counts.StatusEndedCount, totalBets)
return headline, plain, html
}
partsPlain := []string{}
partsHTML := []string{}
if counts.StatusNotFinishedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Incomplete Events (%d Bets)", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Incomplete Events</strong> (%d Bets)</li>", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
}
if counts.StatusToBeFixedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Requires Review (%d Bets)", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Requires Review</strong> (%d Bets)</li>", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
}
if counts.StatusPostponedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Postponed Events (%d Bets)", counts.StatusPostponedCount, counts.StatusPostponedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Postponed Events</strong> (%d Bets)</li>", counts.StatusPostponedCount, counts.StatusPostponedBets))
}
if counts.StatusRemovedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Discarded Events (%d Bets)", counts.StatusRemovedCount, counts.StatusRemovedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Discarded Events</strong> (%d Bets)</li>", counts.StatusRemovedCount, counts.StatusRemovedBets))
}
if counts.StatusEndedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Successfully Ended Events (%d Bets)", counts.StatusEndedCount, counts.StatusEndedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Successfully Ended Events</strong> (%d Bets)</li>", counts.StatusEndedCount, counts.StatusEndedBets))
}
headline := "⚠️ Weekly Results Report — Review Required"
plain := fmt.Sprintf(`%s
Weekly Results Summary (%s):
%s
Totals:
- %d Events Processed
- %d Total Bets
Next Steps:
Some events require your attention. Please log into the admin dashboard to review pending issues.
Best regards,
The System`,
greeting,
period,
strings.Join(partsPlain, "\n"),
totalEvents,
totalBets,
)
html := fmt.Sprintf(`<p>%s</p>
<h2>Weekly Results Summary</h2>
<p><em>Period: %s</em></p>
<ul>
%s
</ul>
<h3>Totals</h3>
<ul>
<li><strong>%d Events Processed</strong></li>
<li><strong>%d Total Bets</strong></li>
</ul>
<p><strong>Next Steps:</strong><br>Some events require your attention. Please <a href="https://admin.fortunebets.net">log into the admin dashboard</a> to review pending issues.</p>
<p>Best regards,<br>The System</p>`,
greeting,
period,
strings.Join(partsHTML, "\n"),
totalEvents,
totalBets,
)
return headline, plain, html
}
func (s *Service) SendAdminResultStatusErrorNotification(
ctx context.Context,
counts domain.ResultLog,
createdAfter time.Time,
endTime time.Time,
) error {
superAdmins, _, 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))
return err
}
metaBytes, err := json.Marshal(counts)
if err != nil {
s.mongoLogger.Error("failed to marshal metadata", zap.Error(err))
return err
}
headline, message := buildHeadlineAndMessage(counts, createdAfter, endTime)
notification := &domain.Notification{
ErrorSeverity: domain.NotificationErrorSeverityHigh,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
Level: domain.NotificationLevelWarning,
Reciever: domain.NotificationRecieverSideAdmin,
DeliveryChannel: domain.DeliveryChannelInApp,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
Priority: 2,
Metadata: metaBytes,
}
var sendErrors []error
for _, user := range superAdmins {
notification.RecipientID = user.ID
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
s.mongoLogger.Error("failed to send admin notification",
zap.Int64("admin_id", user.ID),
zap.Error(err),
)
sendErrors = append(sendErrors, err)
}
// notification.DeliveryChannel = domain.DeliveryChannelEmail
if user.Email == "" {
continue
}
subject, plain, html := buildHeadlineAndMessageEmail(counts, user, createdAfter, endTime)
if err := s.messengerSvc.SendEmail(ctx, user.Email, plain, html, subject); err != nil {
s.mongoLogger.Error("failed to send admin result report email",
zap.Int64("admin_id", user.ID),
zap.Error(err),
)
sendErrors = append(sendErrors, err)
}
}
if len(sendErrors) > 0 {
return fmt.Errorf("sent with partial failure: %d errors", len(sendErrors))
}
return nil
}

View File

@ -262,7 +262,6 @@ func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error {
eventLogger := s.mongoLogger.With( eventLogger := s.mongoLogger.With(
zap.Int64("eventID", event.ID), zap.Int64("eventID", event.ID),
) )
result, err := s.FetchB365Result(ctx, event.SourceEventID) result, err := s.FetchB365Result(ctx, event.SourceEventID)
if err != nil { if err != nil {
if err == ErrEventIsNotActive { if err == ErrEventIsNotActive {
@ -457,276 +456,6 @@ func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error {
return nil return nil
} }
func (s *Service) CheckAndSendResultNotifications(ctx context.Context, createdAfter time.Time) error {
resultLog, err := s.repo.GetAllResultLog(ctx, domain.ResultLogFilter{
CreatedAfter: domain.ValidTime{
Value: createdAfter,
Valid: true,
},
})
if err != nil {
s.mongoLogger.Error(
"Failed to get result log",
zap.Time("CreatedAfter", createdAfter),
zap.Error(err),
)
return err
}
if len(resultLog) == 0 {
s.mongoLogger.Info(
"No results found for check and send result notification",
zap.Time("CreatedAfter", createdAfter),
)
return nil
}
totalResultLog := domain.ResultLog{
StatusNotFinishedCount: resultLog[0].StatusNotFinishedCount,
StatusPostponedCount: resultLog[0].StatusPostponedCount,
}
for _, log := range resultLog {
// Add all the bets
totalResultLog.StatusNotFinishedBets += log.StatusNotFinishedBets
totalResultLog.StatusPostponedBets += log.StatusPostponedBets
totalResultLog.StatusToBeFixedBets += log.StatusToBeFixedBets
totalResultLog.StatusRemovedBets += log.StatusRemovedBets
totalResultLog.StatusEndedBets += log.StatusEndedBets
totalResultLog.StatusToBeFixedCount += log.StatusToBeFixedCount
totalResultLog.StatusRemovedCount += log.StatusRemovedCount
totalResultLog.StatusEndedCount += log.StatusEndedCount
totalResultLog.RemovedCount += log.RemovedCount
}
err = s.SendAdminResultStatusErrorNotification(ctx, totalResultLog)
if err != nil {
s.mongoLogger.Error(
"Failed to send admin result status notification",
zap.Time("CreatedAfter", createdAfter),
zap.Error(err),
)
return err
}
return nil
}
func buildHeadlineAndMessage(counts domain.ResultLog) (string, string) {
totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount
totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets
if totalIssues == 0 {
return "✅ Successfully Processed Event Results", fmt.Sprintf(
"%d total ended events with %d total bets. No issues detected", counts.StatusEndedCount, totalBets,
)
}
parts := []string{}
if counts.StatusNotFinishedCount > 0 {
parts = append(parts, fmt.Sprintf("%d unfinished with %d bets", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
}
if counts.StatusToBeFixedCount > 0 {
parts = append(parts, fmt.Sprintf("%d to-fix with %d bets", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
}
if counts.StatusPostponedCount > 0 {
parts = append(parts, fmt.Sprintf("%d postponed with %d bets", counts.StatusPostponedCount, counts.StatusPostponedBets))
}
if counts.StatusRemovedCount > 0 {
parts = append(parts, fmt.Sprintf("%d removed with %d bets", counts.StatusRemovedCount, counts.StatusRemovedBets))
}
if counts.StatusEndedCount > 0 {
parts = append(parts, fmt.Sprintf("%d ended with %d bets", counts.StatusEndedCount, counts.StatusEndedBets))
}
headline := "⚠️ Issues Found Processing Event Results"
message := fmt.Sprintf("Processed expired event results: %s. Please review pending entries.", strings.Join(parts, ", "))
return headline, message
}
func buildHeadlineAndMessageEmail(counts domain.ResultLog, user domain.User) (string, string, string) {
totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount +
counts.StatusPostponedCount + counts.StatusRemovedCount
totalEvents := counts.StatusEndedCount + counts.StatusNotFinishedCount +
counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount
totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets +
counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets
greeting := fmt.Sprintf("Hi %s %s,", user.FirstName, user.LastName)
if totalIssues == 0 {
headline := "✅ Weekly Results Report — All Events Processed Successfully"
plain := fmt.Sprintf(`%s
Weekly Results Summary:
- %d Ended Events
- %d Total Bets
All events were processed successfully, and no issues were detected.
Best regards,
The System`, greeting, counts.StatusEndedCount, totalBets)
html := fmt.Sprintf(`<p>%s</p>
<h2>Weekly Results Summary</h2>
<ul>
<li><strong>%d Ended Events</strong></li>
<li><strong>%d Total Bets</strong></li>
</ul>
<p>All events were processed successfully, and no issues were detected.</p>
<p>Best regards,<br>The System</p>`,
greeting, counts.StatusEndedCount, totalBets)
return headline, plain, html
}
partsPlain := []string{}
partsHTML := []string{}
if counts.StatusNotFinishedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Incomplete Events (%d Bets)", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Incomplete Events</strong> (%d Bets)</li>", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
}
if counts.StatusToBeFixedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Requires Review (%d Bets)", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Requires Review</strong> (%d Bets)</li>", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets))
}
if counts.StatusPostponedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Postponed Events (%d Bets)", counts.StatusPostponedCount, counts.StatusPostponedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Postponed Events</strong> (%d Bets)</li>", counts.StatusPostponedCount, counts.StatusPostponedBets))
}
if counts.StatusRemovedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Discarded Events (%d Bets)", counts.StatusRemovedCount, counts.StatusRemovedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Discarded Events</strong> (%d Bets)</li>", counts.StatusRemovedCount, counts.StatusRemovedBets))
}
if counts.StatusEndedCount > 0 {
partsPlain = append(partsPlain,
fmt.Sprintf("- %d Successfully Ended Events (%d Bets)", counts.StatusEndedCount, counts.StatusEndedBets))
partsHTML = append(partsHTML,
fmt.Sprintf("<li><strong>%d Successfully Ended Events</strong> (%d Bets)</li>", counts.StatusEndedCount, counts.StatusEndedBets))
}
headline := "⚠️ Weekly Results Report — Review Required"
plain := fmt.Sprintf(`%s
Weekly Results Summary:
%s
Totals:
- %d Events Processed
- %d Total Bets
Next Steps:
Some events require your attention. Please log into the admin dashboard to review pending issues.
Best regards,
The System`,
greeting,
strings.Join(partsPlain, "\n"),
totalEvents,
totalBets,
)
html := fmt.Sprintf(`<p>%s</p>
<h2>Weekly Results Summary</h2>
<ul>
%s
</ul>
<h3>Totals</h3>
<ul>
<li><strong>%d Events Processed</strong></li>
<li><strong>%d Total Bets</strong></li>
</ul>
<p><strong>Next Steps:</strong><br>Some events require your attention. Please <a href="https://admin.fortunebets.net">log into the admin dashboard</a> to review pending issues.</p>
<p>Best regards,<br>The System</p>`,
greeting,
strings.Join(partsHTML, "\n"),
totalEvents,
totalBets,
)
return headline, plain, html
}
func (s *Service) SendAdminResultStatusErrorNotification(
ctx context.Context,
counts domain.ResultLog,
) error {
superAdmins, _, 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))
return err
}
metaBytes, err := json.Marshal(counts)
if err != nil {
s.mongoLogger.Error("failed to marshal metadata", zap.Error(err))
return err
}
headline, message := buildHeadlineAndMessage(counts)
notification := &domain.Notification{
ErrorSeverity: domain.NotificationErrorSeverityHigh,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Type: domain.NOTIFICATION_TYPE_BET_RESULT,
Level: domain.NotificationLevelWarning,
Reciever: domain.NotificationRecieverSideAdmin,
DeliveryChannel: domain.DeliveryChannelInApp,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
Priority: 2,
Metadata: metaBytes,
}
var sendErrors []error
for _, user := range superAdmins {
notification.RecipientID = user.ID
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
s.mongoLogger.Error("failed to send admin notification",
zap.Int64("admin_id", user.ID),
zap.Error(err),
)
sendErrors = append(sendErrors, err)
}
// notification.DeliveryChannel = domain.DeliveryChannelEmail
if user.Email == "" {
continue
}
subject, plain, html := buildHeadlineAndMessageEmail(counts, user)
if err := s.messengerSvc.SendEmail(ctx, user.Email, plain, html, subject); err != nil {
s.mongoLogger.Error("failed to send admin result report email",
zap.Int64("admin_id", user.ID),
zap.Error(err),
)
sendErrors = append(sendErrors, err)
}
}
if len(sendErrors) > 0 {
return fmt.Errorf("sent with partial failure: %d errors", len(sendErrors))
}
return nil
}
func (s *Service) CheckAndUpdateExpiredB365Events(ctx context.Context) (int64, error) { func (s *Service) CheckAndUpdateExpiredB365Events(ctx context.Context) (int64, error) {
events, _, err := s.repo.GetAllEvents(ctx, domain.EventFilter{ events, _, err := s.repo.GetAllEvents(ctx, domain.EventFilter{
LastStartTime: domain.ValidTime{ LastStartTime: domain.ValidTime{

View File

@ -54,7 +54,7 @@ func (s *Service) CreateShopDeposit(ctx context.Context, userID int64, role doma
// } // }
newTransaction, err := s.CreateShopTransaction(ctx, domain.CreateShopTransaction{ newTransaction, err := s.CreateShopTransaction(ctx, domain.CreateShopTransaction{
Amount: domain.Currency(req.Amount), Amount: domain.ToCurrency(req.Amount),
BranchID: branchID, BranchID: branchID,
CompanyID: companyID, CompanyID: companyID,
UserID: userID, UserID: userID,

View File

@ -23,7 +23,7 @@ type VirtualGameService interface {
ProcessTournamentWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) ProcessTournamentWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error)
ProcessPromoWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) ProcessPromoWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error)
GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) // GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error) ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error)
RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error) RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error)
AddFavoriteGame(ctx context.Context, userID, gameID int64) error AddFavoriteGame(ctx context.Context, userID, gameID int64) error

View File

@ -657,9 +657,9 @@ func (s *service) verifySignature(callback *domain.PopOKCallback) bool {
return expected == callback.Signature return expected == callback.Signature
} }
func (s *service) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) { // func (s *service) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) {
return s.repo.GetGameCounts(ctx, filter) // return s.repo.GetGameCounts(ctx, filter)
} // }
func (s *service) ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error) { func (s *service) ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error) {
now := time.Now().Format("02-01-2006 15:04:05") // dd-mm-yyyy hh:mm:ss now := time.Now().Format("02-01-2006 15:04:05") // dd-mm-yyyy hh:mm:ss

View File

@ -8,7 +8,6 @@ import (
"time" "time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/event"
) )
// InitiateDirectDeposit creates a pending deposit request // InitiateDirectDeposit creates a pending deposit request
@ -73,8 +72,8 @@ func (s *Service) VerifyDirectDeposit(
} }
// Publish wallet update event // Publish wallet update event
go s.publishWalletUpdate(ctx, deposit.WalletID, deposit.Wallet.UserID, // go s.publishWalletUpdate(ctx, deposit.WalletID, deposit.Wallet.UserID,
deposit.Wallet.Balance+deposit.Amount, "direct_deposit_verified") // deposit.Wallet.Balance+deposit.Amount, "direct_deposit_verified")
// Update deposit status // Update deposit status
deposit.Status = domain.DepositStatusCompleted deposit.Status = domain.DepositStatusCompleted
@ -206,12 +205,12 @@ func (s *Service) notifyCustomerVerificationResult(ctx context.Context, deposit
} }
} }
func (s *Service) publishWalletUpdate(ctx context.Context, walletID, userID int64, newBalance domain.Currency, trigger string) { // func (s *Service) publishWalletUpdate(ctx context.Context, walletID, userID int64, newBalance domain.Currency, trigger string) {
s.kafkaProducer.Publish(ctx, fmt.Sprint(walletID), event.WalletEvent{ // s.kafkaProducer.Publish(ctx, fmt.Sprint(walletID), event.WalletEvent{
EventType: event.WalletBalanceUpdated, // EventType: event.WalletBalanceUpdated,
WalletID: walletID, // WalletID: walletID,
UserID: userID, // UserID: userID,
Balance: newBalance, // Balance: newBalance,
Trigger: trigger, // Trigger: trigger,
}) // })
} // }

View File

@ -2,10 +2,12 @@ package wallet
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"go.uber.org/zap" "go.uber.org/zap"
"time"
) )
func (s *Service) GetAdminNotificationRecipients(ctx context.Context, walletID int64, walletType domain.WalletType) ([]int64, error) { func (s *Service) GetAdminNotificationRecipients(ctx context.Context, walletID int64, walletType domain.WalletType) ([]int64, error) {
@ -63,12 +65,65 @@ func (s *Service) GetAdminNotificationRecipients(ctx context.Context, walletID i
return recipients, nil return recipients, nil
} }
func (s *Service) SendWalletUpdateNotification(ctx context.Context, wallet domain.Wallet) error {
raw, _ := json.Marshal(map[string]any{
"balance": wallet.Balance.Float32(),
"type": wallet.Type,
"timestamp": time.Now(),
})
headline := ""
message := ""
var receiver domain.NotificationRecieverSide
switch wallet.Type {
case domain.StaticWalletType:
headline = "Referral and Bonus Wallet Updated"
message = fmt.Sprintf("Your referral and bonus wallet balance is now %.2f", wallet.Balance.Float32())
receiver = domain.NotificationRecieverSideCustomer
case domain.RegularWalletType:
headline = "Wallet Updated"
message = fmt.Sprintf("Your wallet balance is now %.2f", wallet.Balance.Float32())
receiver = domain.NotificationRecieverSideCustomer
case domain.BranchWalletType:
headline = "Branch Wallet Updated"
message = fmt.Sprintf("branch wallet balance is now %.2f", wallet.Balance.Float32())
receiver = domain.NotificationRecieverSideBranchManager
case domain.CompanyWalletType:
headline = "Company Wallet Updated"
message = fmt.Sprintf("company wallet balance is now %.2f", wallet.Balance.Float32())
receiver = domain.NotificationRecieverSideAdmin
}
// Handle the wallet event: send notification
notification := &domain.Notification{
RecipientID: wallet.UserID,
DeliveryChannel: domain.DeliveryChannelInApp,
Reciever: receiver,
Type: domain.NotificationTypeWalletUpdated,
DeliveryStatus: domain.DeliveryStatusPending,
IsRead: false,
Level: domain.NotificationLevelInfo,
Priority: 2,
Metadata: raw,
Payload: domain.NotificationPayload{
Headline: headline,
Message: message,
},
}
if err := s.notificationSvc.SendNotification(ctx, notification); err != nil {
s.mongoLogger.Error("[WalletSvc.SendWalletUpdateNotification] Failed to send notification",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
return nil
}
func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet) error { func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet) error {
// Send different messages // Send different messages
// Send notification to admin team // Send notification to admin team
adminNotification := &domain.Notification{ adminNotification := &domain.Notification{
ErrorSeverity: "low", ErrorSeverity: "low",
@ -115,16 +170,16 @@ func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWalle
) )
} }
adminNotification.DeliveryChannel = domain.DeliveryChannelEmail // adminNotification.DeliveryChannel = domain.DeliveryChannelEmail
if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { // if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
s.mongoLogger.Error("failed to send email admin notification", // s.mongoLogger.Error("failed to send email admin notification",
zap.Int64("admin_id", adminID), // zap.Int64("admin_id", adminID),
zap.Error(err), // zap.Error(err),
zap.Time("timestamp", time.Now()), // zap.Time("timestamp", time.Now()),
) // )
return err // return err
} // }
} }
return nil return nil
} }

View File

@ -3,7 +3,7 @@ package wallet
import ( import (
"log/slog" "log/slog"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/kafka" // "github.com/SamuelTariku/FortuneBet-Backend/internal/services/kafka"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"go.uber.org/zap" "go.uber.org/zap"
@ -18,7 +18,6 @@ type Service struct {
userSvc *user.Service userSvc *user.Service
mongoLogger *zap.Logger mongoLogger *zap.Logger
logger *slog.Logger logger *slog.Logger
kafkaProducer *kafka.Producer
} }
func NewService( func NewService(
@ -29,7 +28,6 @@ func NewService(
userSvc *user.Service, userSvc *user.Service,
mongoLogger *zap.Logger, mongoLogger *zap.Logger,
logger *slog.Logger, logger *slog.Logger,
kafkaProducer *kafka.Producer,
) *Service { ) *Service {
return &Service{ return &Service{
walletStore: walletStore, walletStore: walletStore,
@ -40,6 +38,5 @@ func NewService(
userSvc: userSvc, userSvc: userSvc,
mongoLogger: mongoLogger, mongoLogger: mongoLogger,
logger: logger, logger: logger,
kafkaProducer: kafkaProducer,
} }
} }

View File

@ -52,9 +52,11 @@ func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiver
senderWallet, err := s.GetWalletByID(ctx, senderID) senderWallet, err := s.GetWalletByID(ctx, senderID)
if err != nil { if err != nil {
return domain.Transfer{}, err return domain.Transfer{}, err
} }
if !senderWallet.IsActive {
return domain.Transfer{}, ErrWalletIsDisabled
}
if !senderWallet.IsTransferable { if !senderWallet.IsTransferable {
fmt.Printf("Error: %d Sender Wallet is not transferable \n", senderWallet.ID) fmt.Printf("Error: %d Sender Wallet is not transferable \n", senderWallet.ID)
@ -65,7 +67,9 @@ func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiver
if err != nil { if err != nil {
return domain.Transfer{}, err return domain.Transfer{}, err
} }
if !receiverWallet.IsActive {
return domain.Transfer{}, ErrWalletIsDisabled
}
if !receiverWallet.IsTransferable { if !receiverWallet.IsTransferable {
fmt.Printf("Error: %d Receiver Wallet is not transferable \n", senderWallet.ID) fmt.Printf("Error: %d Receiver Wallet is not transferable \n", senderWallet.ID)
return domain.Transfer{}, ErrReceiverWalletNotTransferable return domain.Transfer{}, ErrReceiverWalletNotTransferable

View File

@ -3,14 +3,17 @@ package wallet
import ( import (
"context" "context"
"errors" "errors"
"fmt"
// "fmt"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/event" "go.uber.org/zap"
// "github.com/SamuelTariku/FortuneBet-Backend/internal/event"
) )
var ( var (
ErrBalanceInsufficient = errors.New("wallet balance is insufficient") ErrBalanceInsufficient = errors.New("wallet balance is insufficient")
ErrWalletIsDisabled = errors.New("wallet is disabled")
) )
func (s *Service) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) { func (s *Service) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) {
@ -82,26 +85,37 @@ func (s *Service) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWalle
} }
func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error { func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error {
err := s.walletStore.UpdateBalance(ctx, id, balance)
if err != nil {
return err
}
wallet, err := s.GetWalletByID(ctx, id) wallet, err := s.GetWalletByID(ctx, id)
if err != nil { if err != nil {
return err return err
} }
go func() { if !wallet.IsActive {
s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.UserID), event.WalletEvent{ return ErrWalletIsDisabled
EventType: event.WalletBalanceUpdated, }
WalletID: wallet.ID,
UserID: wallet.UserID, err = s.walletStore.UpdateBalance(ctx, id, balance)
Balance: balance, if err != nil {
WalletType: wallet.Type, return err
Trigger: "UpdateBalance", }
})
}() // go func() {
// s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.UserID), event.WalletEvent{
// EventType: event.WalletBalanceUpdated,
// WalletID: wallet.ID,
// UserID: wallet.UserID,
// Balance: balance,
// WalletType: wallet.Type,
// Trigger: "UpdateBalance",
// })
// }()
if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil {
s.mongoLogger.Info("Failed to send wallet update notification",
zap.Int64("wallet_id", wallet.ID),
zap.Error(err))
}
return nil return nil
} }
@ -112,22 +126,29 @@ func (s *Service) AddToWallet(
if err != nil { if err != nil {
return domain.Transfer{}, err return domain.Transfer{}, err
} }
if !wallet.IsActive {
return domain.Transfer{}, ErrWalletIsDisabled
}
err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount) err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount)
if err != nil { if err != nil {
return domain.Transfer{}, err return domain.Transfer{}, err
} }
go func() { // go func() {
s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{ // s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{
EventType: event.WalletBalanceUpdated, // EventType: event.WalletBalanceUpdated,
WalletID: wallet.ID, // WalletID: wallet.ID,
UserID: wallet.UserID, // UserID: wallet.UserID,
Balance: wallet.Balance + amount, // Balance: wallet.Balance + amount,
WalletType: wallet.Type, // WalletType: wallet.Type,
Trigger: "AddToWallet", // Trigger: "AddToWallet",
}) // })
}() // }()
if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil {
s.mongoLogger.Info("Failed to send wallet update notification",
zap.Int64("wallet_id", wallet.ID),
zap.Error(err))
}
// Log the transfer here for reference // Log the transfer here for reference
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{ newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
@ -153,6 +174,9 @@ func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.
return domain.Transfer{}, err return domain.Transfer{}, err
} }
if !wallet.IsActive {
return domain.Transfer{}, ErrWalletIsDisabled
}
if wallet.Balance < amount { if wallet.Balance < amount {
// Send Wallet low to admin // Send Wallet low to admin
if wallet.Type == domain.CompanyWalletType || wallet.Type == domain.BranchWalletType { if wallet.Type == domain.CompanyWalletType || wallet.Type == domain.BranchWalletType {
@ -173,8 +197,11 @@ func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.
} }
balance := wallet.Balance.Float32() balance := wallet.Balance.Float32()
if balance < thresholds[0] { for _, thresholds := range thresholds {
s.SendAdminWalletLowNotification(ctx, wallet) if thresholds < balance && thresholds > (balance-amount.Float32()) {
s.SendAdminWalletLowNotification(ctx, wallet)
break
}
} }
} }
@ -184,16 +211,22 @@ func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.
return domain.Transfer{}, nil return domain.Transfer{}, nil
} }
go func() { // go func() {
s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{ // s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{
EventType: event.WalletBalanceUpdated, // EventType: event.WalletBalanceUpdated,
WalletID: wallet.ID, // WalletID: wallet.ID,
UserID: wallet.UserID, // UserID: wallet.UserID,
Balance: wallet.Balance - amount, // Balance: wallet.Balance - amount,
WalletType: wallet.Type, // WalletType: wallet.Type,
Trigger: "DeductFromWallet", // Trigger: "DeductFromWallet",
}) // })
}() // }()
if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil {
s.mongoLogger.Info("Failed to send wallet update notification",
zap.Int64("wallet_id", wallet.ID),
zap.Error(err))
}
// Log the transfer here for reference // Log the transfer here for reference
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{ newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{

View File

@ -11,6 +11,7 @@ import (
betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse" enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/report" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/report"
resultsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result" resultsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result"
@ -108,7 +109,7 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
mongoLogger.Info("Cron jobs started for event and odds services") mongoLogger.Info("Cron jobs started for event and odds services")
} }
func StartTicketCrons(ticketService ticket.Service, mongoLogger *zap.Logger) { func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) {
c := cron.New(cron.WithSeconds()) c := cron.New(cron.WithSeconds())
schedule := []struct { schedule := []struct {
@ -128,6 +129,19 @@ func StartTicketCrons(ticketService ticket.Service, mongoLogger *zap.Logger) {
} }
}, },
}, },
{
spec: "0 0 0 * * 1", // Every Monday (Weekly)
task: func() {
mongoLogger.Info("Deleting old notifications")
if err := notificationSvc.DeleteOldNotifications(context.Background()); err != nil {
mongoLogger.Error("Failed to remove old notifications",
zap.Error(err),
)
} else {
mongoLogger.Info("Successfully deleted old notifications")
}
},
},
} }
for _, job := range schedule { for _, job := range schedule {
@ -261,7 +275,7 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreSports(ctx); err != nil { if err := enetPulseSvc.FetchAndStoreSports(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store sports", zap.Error(err)) mongoLogger.Error("Failed to fetch and store sports", zap.Error(err))
} else { } else {
mongoLogger.Info("✅ Completed fetching and storing sports") mongoLogger.Info("\n\n✅ Completed fetching and storing sports\n\n")
} }
// 2⃣ Tournament Templates // 2⃣ Tournament Templates
@ -269,7 +283,7 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreTournamentTemplates(ctx); err != nil { if err := enetPulseSvc.FetchAndStoreTournamentTemplates(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store tournament templates", zap.Error(err)) mongoLogger.Error("Failed to fetch and store tournament templates", zap.Error(err))
} else { } else {
mongoLogger.Info("✅ Completed fetching and storing tournament templates") mongoLogger.Info("\n\n✅ Completed fetching and storing tournament templates\n\n")
} }
// 3⃣ Tournaments // 3⃣ Tournaments
@ -277,7 +291,7 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreTournaments(ctx); err != nil { if err := enetPulseSvc.FetchAndStoreTournaments(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store tournaments", zap.Error(err)) mongoLogger.Error("Failed to fetch and store tournaments", zap.Error(err))
} else { } else {
mongoLogger.Info("✅ Completed fetching and storing tournaments") mongoLogger.Info("\n\n✅ Completed fetching and storing tournaments\n\n")
} }
// 4⃣ Tournament Stages // 4⃣ Tournament Stages
@ -285,16 +299,16 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreTournamentStages(ctx); err != nil { if err := enetPulseSvc.FetchAndStoreTournamentStages(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store tournament stages", zap.Error(err)) mongoLogger.Error("Failed to fetch and store tournament stages", zap.Error(err))
} else { } else {
mongoLogger.Info("✅ Completed fetching and storing tournament stages") mongoLogger.Info("✅ \n\nCompleted fetching and storing tournament stages\n\n")
} }
// 5⃣ Fixtures // // 5⃣ Fixtures
mongoLogger.Info("Began fetching and storing fixtures cron task") mongoLogger.Info("Began fetching and storing fixtures cron task")
today := time.Now().Format("2006-01-02") today := time.Now().Format("2006-01-02")
if err := enetPulseSvc.FetchAndStoreFixtures(ctx, today); err != nil { if err := enetPulseSvc.FetchAndStoreFixtures(ctx, today); err != nil {
mongoLogger.Error("Failed to fetch and store fixtures", zap.Error(err)) mongoLogger.Error("Failed to fetch and store fixtures", zap.Error(err))
} else { } else {
mongoLogger.Info("✅ Completed fetching and storing fixtures") mongoLogger.Info("\n\n✅ Completed fetching and storing fixtures\n\n")
} }
// 6⃣ Results // 6⃣ Results
@ -302,7 +316,7 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreResults(ctx); err != nil { if err := enetPulseSvc.FetchAndStoreResults(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store results", zap.Error(err)) mongoLogger.Error("Failed to fetch and store results", zap.Error(err))
} else { } else {
mongoLogger.Info("✅ Completed fetching and storing results") mongoLogger.Info("\n\n✅ Completed fetching and storing results\n\n")
} }
// 7 Outcome Types // 7 Outcome Types
@ -310,15 +324,15 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreOutcomeTypes(ctx); err != nil { if err := enetPulseSvc.FetchAndStoreOutcomeTypes(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store outcome_types", zap.Error(err)) mongoLogger.Error("Failed to fetch and store outcome_types", zap.Error(err))
} else { } else {
mongoLogger.Info("✅ Completed fetching and storing outcome_types") mongoLogger.Info("\n\n✅ Completed fetching and storing outcome_types\n\n")
} }
// 8 Outcome Types // 8 Preodds
mongoLogger.Info("Began fetching and storing preodds cron task") mongoLogger.Info("Began fetching and storing preodds cron task")
if err := enetPulseSvc.FetchAndStorePreodds(ctx); err != nil { if err := enetPulseSvc.FetchAndStorePreodds(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store preodds", zap.Error(err)) mongoLogger.Error("Failed to fetch and store preodds", zap.Error(err))
} else { } else {
mongoLogger.Info("✅ Completed fetching and storing preodds") mongoLogger.Info("\n\n✅ Completed fetching and storing preodds\n\n")
} }
}, },
}, },

View File

@ -103,25 +103,25 @@ func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create admin:"+err.Error()) return fiber.NewError(fiber.StatusInternalServerError, "Failed to create admin:"+err.Error())
} }
if req.CompanyID != nil { // if req.CompanyID != nil {
_, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{ // _, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
ID: *req.CompanyID, // ID: *req.CompanyID,
AdminID: domain.ValidInt64{ // AdminID: domain.ValidInt64{
Value: newUser.ID, // Value: newUser.ID,
Valid: true, // Valid: true,
}, // },
}) // })
if err != nil { // if err != nil {
h.mongoLoggerSvc.Error("failed to update company with new admin", // h.mongoLoggerSvc.Error("failed to update company with new admin",
zap.Int64("status_code", fiber.StatusInternalServerError), // zap.Int64("status_code", fiber.StatusInternalServerError),
zap.Int64("company_id", *req.CompanyID), // zap.Int64("company_id", *req.CompanyID),
zap.Int64("admin_id", newUser.ID), // zap.Int64("admin_id", newUser.ID),
zap.Error(err), // zap.Error(err),
zap.Time("timestamp", time.Now()), // zap.Time("timestamp", time.Now()),
) // )
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company"+err.Error()) // return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company"+err.Error())
} // }
} // }
h.mongoLoggerSvc.Info("admin created successfully", h.mongoLoggerSvc.Info("admin created successfully",
zap.Int64("admin_id", newUser.ID), zap.Int64("admin_id", newUser.ID),
@ -196,7 +196,7 @@ func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
Valid: true, Valid: true,
} }
} }
companyFilter := int64(c.QueryInt("company_id")) companyFilter := int64(c.QueryInt("company_id"))
filter := domain.UserFilter{ filter := domain.UserFilter{
Role: string(domain.RoleAdmin), Role: string(domain.RoleAdmin),
@ -283,7 +283,7 @@ func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
zap.Time("timestamp", time.Now()), zap.Time("timestamp", time.Now()),
) )
return response.WritePaginatedJSON(c, fiber.StatusOK, "Admins retrieved successfully", result, nil, filter.Page.Value, int(total)) return response.WritePaginatedJSON(c, fiber.StatusOK, "Admins retrieved successfully", result, nil, filter.Page.Value+1, int(total))
} }
// GetAdminByID godoc // GetAdminByID godoc
@ -451,24 +451,24 @@ func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update admin:"+err.Error()) return fiber.NewError(fiber.StatusInternalServerError, "Failed to update admin:"+err.Error())
} }
if req.CompanyID != nil { // if req.CompanyID != nil {
_, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{ // _, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
ID: *req.CompanyID, // ID: *req.CompanyID,
AdminID: domain.ValidInt64{ // AdminID: domain.ValidInt64{
Value: AdminID, // Value: AdminID,
Valid: true, // Valid: true,
}, // },
}) // })
if err != nil { // if err != nil {
h.mongoLoggerSvc.Error("UpdateAdmin failed to update company", // h.mongoLoggerSvc.Error("UpdateAdmin failed to update company",
zap.Int("status_code", fiber.StatusInternalServerError), // zap.Int("status_code", fiber.StatusInternalServerError),
zap.Int64("admin_id", AdminID), // zap.Int64("admin_id", AdminID),
zap.Error(err), // zap.Error(err),
zap.Time("timestamp", time.Now()), // zap.Time("timestamp", time.Now()),
) // )
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company:"+err.Error()) // return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company:"+err.Error())
} // }
} // }
h.mongoLoggerSvc.Info("UpdateAdmin succeeded", h.mongoLoggerSvc.Info("UpdateAdmin succeeded",
zap.Int("status_code", fiber.StatusOK), zap.Int("status_code", fiber.StatusOK),

View File

@ -169,7 +169,7 @@ type loginAdminRes struct {
func (h *Handler) LoginAdmin(c *fiber.Ctx) error { func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64) companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid { if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id") h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id") return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
} }
var req loginAdminReq var req loginAdminReq

View File

@ -259,24 +259,53 @@ func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userI
sportAndLeagueIDs = append(sportAndLeagueIDs, ids) sportAndLeagueIDs = append(sportAndLeagueIDs, ids)
} }
fmt.Println("sportAndLeagueIDs: ", sportAndLeagueIDs)
for _, raffle := range raffles { for _, raffle := range raffles {
// TODO: only fetch pending raffles from db // TODO: only fetch pending raffles from db
if raffle.Status == "completed" { if raffle.Status == "completed" {
continue continue
} }
// only require one sport and league combo to be valide to make the raffle ticket raffleTicketLimit, err := h.raffleSvc.GetRaffleTicketLimit(c.Context(), raffle.ID)
foundValid := false if err != nil {
continue
}
// check raffle ticke count
userTicketCount, err := h.raffleSvc.GetRaffleTicketCount(c.Context(), raffle.ID, int32(userID))
if err != nil {
continue
}
if userTicketCount == int64(raffleTicketLimit) {
h.mongoLoggerSvc.Info("User reached max ticket count allowed for current raffle",
zap.Int("status_code", fiber.StatusForbidden),
zap.Int64("raffleID", int64(raffle.ID)),
zap.Int64("userID", userID),
zap.Int64("companyID", companyID),
zap.Time("timestamp", time.Now()),
)
continue
}
// empty raffle filter means there is no filter (all is allowed)
hasFilter, err := h.raffleSvc.CheckSportRaffleHasFilter(c.Context(), raffle.ID)
if err != nil {
continue
}
foundValid := !hasFilter
// only require one sport and league combo to be valid to make the raffle ticket
for _, sportAndLeagueID := range sportAndLeagueIDs { for _, sportAndLeagueID := range sportAndLeagueIDs {
if foundValid {
break
}
res, err := h.raffleSvc.CheckValidSportRaffleFilter(c.Context(), raffle.ID, sportAndLeagueID[0], sportAndLeagueID[1]) res, err := h.raffleSvc.CheckValidSportRaffleFilter(c.Context(), raffle.ID, sportAndLeagueID[0], sportAndLeagueID[1])
if err != nil { if err != nil {
continue continue
} }
fmt.Println(sportAndLeagueID, res)
foundValid = foundValid || res foundValid = foundValid || res
} }
@ -289,7 +318,7 @@ func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userI
UserID: int32(userID), UserID: int32(userID),
} }
_, err := h.raffleSvc.CreateRaffleTicket(c.Context(), raffleTicket) _, err = h.raffleSvc.CreateRaffleTicket(c.Context(), raffleTicket)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to create raffle ticket", h.mongoLoggerSvc.Error("Failed to create raffle ticket",
zap.Int("status_code", fiber.StatusInternalServerError), zap.Int("status_code", fiber.StatusInternalServerError),
@ -552,6 +581,25 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
Valid: true, Valid: true,
} }
} }
var companyID domain.ValidInt64
companyIDQuery := c.Query("company_id")
if companyIDQuery != "" {
companyIDParsed, err := strconv.ParseInt(companyIDQuery, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("invalid company_id format",
zap.String("company_id", companyIDQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid company_id format")
}
companyID = domain.ValidInt64{
Value: companyIDParsed,
Valid: true,
}
}
bets, total, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{ bets, total, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{
IsShopBet: isShopBet, IsShopBet: isShopBet,
Query: searchString, Query: searchString,
@ -560,6 +608,7 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
Status: statusFilter, Status: statusFilter,
Limit: limit, Limit: limit,
Offset: offset, Offset: offset,
CompanyID: companyID,
}) })
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to get all bets", h.mongoLoggerSvc.Error("Failed to get all bets",
@ -594,7 +643,6 @@ func (h *Handler) GetAllTenantBets(c *fiber.Ctx) error {
h.BadRequestLogger().Error("invalid company id", zap.Any("company_id", companyID)) h.BadRequestLogger().Error("invalid company id", zap.Any("company_id", companyID))
return fiber.NewError(fiber.StatusBadRequest, "invalid company id") return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
} }
role := c.Locals("role").(domain.Role)
page := c.QueryInt("page", 1) page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10) pageSize := c.QueryInt("page_size", 10)
@ -608,7 +656,7 @@ func (h *Handler) GetAllTenantBets(c *fiber.Ctx) error {
} }
var isShopBet domain.ValidBool var isShopBet domain.ValidBool
isShopBetQuery := c.Query("is_shop") isShopBetQuery := c.Query("is_shop")
if isShopBetQuery != "" && role == domain.RoleSuperAdmin { if isShopBetQuery != "" {
isShopBetParse, err := strconv.ParseBool(isShopBetQuery) isShopBetParse, err := strconv.ParseBool(isShopBetQuery)
if err != nil { if err != nil {
h.mongoLoggerSvc.Info("failed to parse is_shop_bet", h.mongoLoggerSvc.Info("failed to parse is_shop_bet",
@ -726,6 +774,8 @@ func (h *Handler) GetAllTenantBets(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/bet/{id} [get] // @Router /api/v1/sport/bet/{id} [get]
func (h *Handler) GetBetByID(c *fiber.Ctx) error { func (h *Handler) GetBetByID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
betID := c.Params("id") betID := c.Params("id")
id, err := strconv.ParseInt(betID, 10, 64) id, err := strconv.ParseInt(betID, 10, 64)
if err != nil { if err != nil {
@ -751,6 +801,15 @@ func (h *Handler) GetBetByID(c *fiber.Ctx) error {
res := domain.ConvertBet(bet) res := domain.ConvertBet(bet)
if companyID.Valid && bet.CompanyID != companyID.Value {
h.mongoLoggerSvc.Warn("Warn - Company is trying to access another companies bet",
zap.Int64("betID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
}
// h.mongoLoggerSvc.Info("Bet retrieved successfully", // h.mongoLoggerSvc.Info("Bet retrieved successfully",
// zap.Int64("betID", id), // zap.Int64("betID", id),
// zap.Int("status_code", fiber.StatusOK), // zap.Int("status_code", fiber.StatusOK),

View File

@ -611,6 +611,8 @@ func (h *Handler) GetAllBranches(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/search/branch [get] // @Router /api/v1/search/branch [get]
func (h *Handler) SearchBranch(c *fiber.Ctx) error { func (h *Handler) SearchBranch(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
// Get search query from request // Get search query from request
searchQuery := c.Query("q") searchQuery := c.Query("q")
if searchQuery == "" { if searchQuery == "" {
@ -622,7 +624,7 @@ func (h *Handler) SearchBranch(c *fiber.Ctx) error {
} }
// Call the service to search for branches // Call the service to search for branches
branches, err := h.branchSvc.SearchBranchByName(c.Context(), searchQuery) branches, err := h.branchSvc.SearchBranchByName(c.Context(), searchQuery, companyID)
if err != nil { if err != nil {
h.mongoLoggerSvc.Info("Failed to search branches", h.mongoLoggerSvc.Info("Failed to search branches",
zap.String("query", searchQuery), zap.String("query", searchQuery),

View File

@ -227,11 +227,8 @@ func (h *Handler) GetAllCashiers(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
cashiers, total, err := h.userSvc.GetAllCashiers(c.Context(), domain.UserFilter{ cashiers, total, err := h.userSvc.GetAllCashiers(c.Context(), filter)
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
})
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("failed to get all cashiers", h.mongoLoggerSvc.Error("failed to get all cashiers",
zap.Int("status_code", fiber.StatusInternalServerError), zap.Int("status_code", fiber.StatusInternalServerError),

View File

@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"errors"
"fmt" "fmt"
"strconv" "strconv"
"time" "time"
@ -82,17 +83,31 @@ func (h *Handler) CreateCompany(c *fiber.Ctx) error {
AdminID: user.ID, AdminID: user.ID,
WalletID: newWallet.ID, WalletID: newWallet.ID,
DeductedPercentage: req.DeductedPercentage, DeductedPercentage: req.DeductedPercentage,
Slug: req.Slug,
IsActive: req.IsActive,
}) })
if err != nil { if err != nil {
if errors.Is(err, domain.ErrWalletIDDuplicate) {
h.mongoLoggerSvc.Error("CreateCompanyReq failed to create company",
zap.Int64("userID", user.ID),
zap.String("name", req.Name),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "this admin already has a company assigned to him")
}
h.mongoLoggerSvc.Error("CreateCompanyReq failed to create company", h.mongoLoggerSvc.Error("CreateCompanyReq failed to create company",
zap.Int64("userID", user.ID), zap.Int64("userID", user.ID),
zap.String("name", req.Name), zap.String("name", req.Name),
zap.String("Name", req.Name),
zap.Int("status_code", fiber.StatusInternalServerError), zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()), zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
@ -361,11 +376,12 @@ func (h *Handler) UpdateCompany(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
company, err := h.companySvc.UpdateCompany(c.Context(), domain.ConvertUpdateCompanyReq(req)) err = h.companySvc.UpdateCompany(c.Context(), domain.ConvertUpdateCompanyReq(req, id))
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to update company", h.mongoLoggerSvc.Error("Failed to update company",
zap.Int64("companyID", id), zap.Int64("companyID", id),
zap.Any("req", req),
zap.Int("status_code", fiber.StatusInternalServerError), zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()), zap.Time("timestamp", time.Now()),
@ -373,9 +389,7 @@ func (h *Handler) UpdateCompany(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
res := domain.ConvertCompany(company) return response.WriteJSON(c, fiber.StatusOK, "Company Updated", nil, nil)
return response.WriteJSON(c, fiber.StatusOK, "Company Updated", res, nil)
} }

View File

@ -234,6 +234,62 @@ func (h *Handler) GetAllResults(c *fiber.Ctx) error {
}) })
} }
// GetAllPreodds godoc
// @Summary Get all preodds
// @Description Fetches all EnetPulse pre-match odds stored in the database
// @Tags EnetPulse - Preodds
// @Accept json
// @Produce json
// @Success 200 {object} domain.Response{data=[]domain.EnetpulsePreodds}
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/enetpulse/preodds [get]
func (h *Handler) GetAllPreodds(c *fiber.Ctx) error {
// Call service
preodds, err := h.enetPulseSvc.GetAllPreodds(c.Context())
if err != nil {
log.Println("GetAllPreodds error:", err)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to fetch EnetPulse preodds",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "EnetPulse preodds fetched successfully",
Data: preodds,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// GetFixturesWithPreodds godoc
// @Summary Get fixtures with preodds
// @Description Fetches all EnetPulse fixtures along with their associated pre-match odds
// @Tags EnetPulse - Fixtures
// @Accept json
// @Produce json
// @Success 200 {object} domain.Response{data=[]domain.EnetpulseFixtureWithPreodds}
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/enetpulse/fixtures/preodds [get]
func (h *Handler) GetFixturesWithPreodds(c *fiber.Ctx) error {
// Call service
fixtures, err := h.enetPulseSvc.GetFixturesWithPreodds(c.Context())
if err != nil {
log.Println("GetFixturesWithPreodds error:", err)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to fetch EnetPulse fixtures with preodds",
Error: err.Error(),
})
}
// Return success response
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "EnetPulse fixtures with preodds fetched successfully",
Data: fixtures,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// Helper: parse comma-separated string into []int // Helper: parse comma-separated string into []int
func parseIntSlice(input string) []int { func parseIntSlice(input string) []int {

View File

@ -135,6 +135,40 @@ func (h *Handler) GetAllEvents(c *fiber.Ctx) error {
} }
} }
isActiveQuery := c.Query("is_active")
var isActive domain.ValidBool
if isActiveQuery != "" {
isActiveParsed, err := strconv.ParseBool(isActiveQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse isActive",
zap.String("is_active", isActiveQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_active")
}
isActive = domain.ValidBool{
Value: isActiveParsed,
Valid: true,
}
}
statusQuery := c.Query("status")
var eventStatus domain.ValidEventStatus
if statusQuery != "" {
eventStatusParsed, err := domain.ParseEventStatus(statusQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse statusQuery",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid event status string")
}
eventStatus = domain.ValidEventStatus{
Value: eventStatusParsed,
Valid: true,
}
}
events, total, err := h.eventSvc.GetAllEvents( events, total, err := h.eventSvc.GetAllEvents(
c.Context(), domain.EventFilter{ c.Context(), domain.EventFilter{
SportID: sportID, SportID: sportID,
@ -146,6 +180,8 @@ func (h *Handler) GetAllEvents(c *fiber.Ctx) error {
Offset: offset, Offset: offset,
CountryCode: countryCode, CountryCode: countryCode,
Featured: isFeatured, Featured: isFeatured,
Active: isActive,
Status: eventStatus,
}) })
// fmt.Printf("League ID: %v", leagueID) // fmt.Printf("League ID: %v", leagueID)
@ -234,22 +270,27 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
Valid: searchQuery != "", Valid: searchQuery != "",
} }
// firstStartTimeQuery := c.Query("first_start_time") firstStartTimeQuery := c.Query("first_start_time")
// var firstStartTime domain.ValidTime var firstStartTime domain.ValidTime
// if firstStartTimeQuery != "" { if firstStartTimeQuery != "" {
// firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery) firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
// if err != nil { if err != nil {
// h.BadRequestLogger().Info("invalid start_time format", h.BadRequestLogger().Info("invalid start_time format",
// zap.String("first_start_time", firstStartTimeQuery), zap.String("first_start_time", firstStartTimeQuery),
// zap.Error(err), zap.Error(err),
// ) )
// return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
// } }
// firstStartTime = domain.ValidTime{ firstStartTime = domain.ValidTime{
// Value: firstStartTimeParsed, Value: firstStartTimeParsed,
// Valid: true, Valid: true,
// } }
// } } else {
firstStartTime = domain.ValidTime{
Value: time.Now(),
Valid: true,
}
}
lastStartTimeQuery := c.Query("last_start_time") lastStartTimeQuery := c.Query("last_start_time")
var lastStartTime domain.ValidTime var lastStartTime domain.ValidTime
@ -297,10 +338,7 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
SportID: sportID, SportID: sportID,
LeagueID: leagueID, LeagueID: leagueID,
Query: searchString, Query: searchString,
FirstStartTime: domain.ValidTime{ FirstStartTime: firstStartTime,
Value: time.Now(),
Valid: true,
},
LastStartTime: lastStartTime, LastStartTime: lastStartTime,
Limit: limit, Limit: limit,
Offset: offset, Offset: offset,
@ -334,6 +372,200 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
} }
// @Summary Retrieve all upcoming events with settings
// @Description Retrieve all upcoming events settings from the database
// @Tags prematch
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Param league_id query string false "League ID Filter"
// @Param sport_id query string false "Sport ID Filter"
// @Param cc query string false "Country Code Filter"
// @Param first_start_time query string false "Start Time"
// @Param last_start_time query string false "End Time"
// @Success 200 {array} domain.BaseEvent
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/events [get]
func (h *Handler) GetTenantEvents(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10)
limit := domain.ValidInt32{
Value: int32(pageSize),
Valid: true,
}
offset := domain.ValidInt32{
Value: int32(page - 1),
Valid: true,
}
leagueIDQuery := c.Query("league_id")
var leagueID domain.ValidInt64
if leagueIDQuery != "" {
leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64)
if err != nil {
h.BadRequestLogger().Error("invalid league id",
zap.String("league_id", leagueIDQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
}
leagueID = domain.ValidInt64{
Value: leagueIDInt,
Valid: true,
}
}
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil {
h.BadRequestLogger().Info("invalid sport id",
zap.String("sportID", sportIDQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid sport id")
}
sportID = domain.ValidInt32{
Value: int32(sportIDint),
Valid: true,
}
}
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
firstStartTimeQuery := c.Query("first_start_time")
var firstStartTime domain.ValidTime
if firstStartTimeQuery != "" {
firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
if err != nil {
h.BadRequestLogger().Info("invalid start_time format",
zap.String("first_start_time", firstStartTimeQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
}
firstStartTime = domain.ValidTime{
Value: firstStartTimeParsed,
Valid: true,
}
}
lastStartTimeQuery := c.Query("last_start_time")
var lastStartTime domain.ValidTime
if lastStartTimeQuery != "" {
lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery)
if err != nil {
h.BadRequestLogger().Info("invalid last_start_time format",
zap.String("last_start_time", lastStartTimeQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
}
lastStartTime = domain.ValidTime{
Value: lastStartTimeParsed,
Valid: true,
}
}
countryCodeQuery := c.Query("cc")
countryCode := domain.ValidString{
Value: countryCodeQuery,
Valid: countryCodeQuery != "",
}
isFeaturedQuery := c.Query("is_featured")
var isFeatured domain.ValidBool
if isFeaturedQuery != "" {
isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse isFeatured",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_featured")
}
isFeatured = domain.ValidBool{
Value: isFeaturedParsed,
Valid: true,
}
}
isActiveQuery := c.Query("is_active")
var isActive domain.ValidBool
if isActiveQuery != "" {
isActiveParsed, err := strconv.ParseBool(isActiveQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse isActive",
zap.String("is_active", isActiveQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_active")
}
isActive = domain.ValidBool{
Value: isActiveParsed,
Valid: true,
}
}
statusQuery := c.Query("status")
var eventStatus domain.ValidEventStatus
if statusQuery != "" {
eventStatusParsed, err := domain.ParseEventStatus(statusQuery)
if err != nil {
h.BadRequestLogger().Error("Failed to parse statusQuery",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid event status string")
}
eventStatus = domain.ValidEventStatus{
Value: eventStatusParsed,
Valid: true,
}
}
events, total, err := h.eventSvc.GetEventsWithSettings(
c.Context(), companyID.Value, domain.EventFilter{
SportID: sportID,
LeagueID: leagueID,
Query: searchString,
FirstStartTime: firstStartTime,
LastStartTime: lastStartTime,
Limit: limit,
Offset: offset,
CountryCode: countryCode,
Featured: isFeatured,
Status: eventStatus,
Active: isActive,
})
// fmt.Printf("League ID: %v", leagueID)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to retrieve all upcoming events",
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
res := domain.ConvertEventWithSettingResList(events)
return response.WritePaginatedJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", res, nil, page, int(total))
}
type TopLeaguesRes struct { type TopLeaguesRes struct {
Leagues []TopLeague `json:"leagues"` Leagues []TopLeague `json:"leagues"`
} }
@ -483,6 +715,145 @@ func (h *Handler) GetTenantEventByID(c *fiber.Ctx) error {
} }
// @Summary Retrieve bet outcomes by event id
// @Description Retrieve bet outcomes by event id
// @Tags prematch
// @Accept json
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} domain.BaseEvent
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/tenant/{tenant_slug}/events/{id}/bets [get]
func (h *Handler) GetTenantBetsByEventID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
idStr := c.Params("id")
eventID, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10)
limit := domain.ValidInt32{
Value: int32(pageSize),
Valid: true,
}
offset := domain.ValidInt32{
Value: int32(page - 1),
Valid: true,
}
statusQuery := c.Params("status")
var status domain.ValidOutcomeStatus
if statusQuery != "" {
statusIntParse, err := strconv.ParseInt(statusQuery, 10, 32)
if err != nil {
h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery))
return fiber.NewError(fiber.StatusBadRequest, "Invalid status query")
}
statusParsed, err := domain.ParseOutcomeStatus(int(statusIntParse))
if err != nil {
h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery))
return fiber.NewError(fiber.StatusBadRequest, "Invalid status query")
}
status = domain.ValidOutcomeStatus{
Value: statusParsed,
Valid: true,
}
}
res, total, err := h.betSvc.GetBetOutcomeViewByEventID(c.Context(), eventID, domain.BetOutcomeViewFilter{
OutcomeStatus: status,
CompanyID: companyID,
Limit: limit,
Offset: offset,
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to get upcoming event by id",
zap.Int64("eventID", eventID),
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WritePaginatedJSON(c, fiber.StatusOK, "Bet Outcomes retrieved successfully", res, nil, page, int(total))
}
// @Summary Retrieve bet outcomes by event id
// @Description Retrieve bet outcomes by event id
// @Tags prematch
// @Accept json
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} domain.BaseEvent
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/events/{id}/bets [get]
func (h *Handler) GetBetsByEventID(c *fiber.Ctx) error {
idStr := c.Params("id")
eventID, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10)
limit := domain.ValidInt32{
Value: int32(pageSize),
Valid: true,
}
offset := domain.ValidInt32{
Value: int32(page - 1),
Valid: true,
}
statusQuery := c.Params("status")
var status domain.ValidOutcomeStatus
if statusQuery != "" {
statusIntParse, err := strconv.ParseInt(statusQuery, 10, 32)
if err != nil {
h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery))
return fiber.NewError(fiber.StatusBadRequest, "Invalid status query")
}
statusParsed, err := domain.ParseOutcomeStatus(int(statusIntParse))
if err != nil {
h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery))
return fiber.NewError(fiber.StatusBadRequest, "Invalid status query")
}
status = domain.ValidOutcomeStatus{
Value: statusParsed,
Valid: true,
}
}
res, total, err := h.betSvc.GetBetOutcomeViewByEventID(c.Context(), eventID, domain.BetOutcomeViewFilter{
OutcomeStatus: status,
Limit: limit,
Offset: offset,
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to get upcoming event by id",
zap.Int64("eventID", eventID),
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WritePaginatedJSON(c, fiber.StatusOK, "Bet Outcomes retrieved successfully", res, nil, page, int(total))
}
type UpdateEventStatusReq struct { type UpdateEventStatusReq struct {
} }
@ -519,9 +890,9 @@ func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error {
} }
type UpdateEventSettingsReq struct { type UpdateEventSettingsReq struct {
Featured *bool `json:"is_featured" example:"true"` Featured *bool `json:"is_featured" example:"true"`
IsActive *bool `json:"is_active" example:"true"` IsActive *bool `json:"is_active" example:"true"`
WinningUpperLimit *int `json:"winning_upper_limit" example:"10000"` WinningUpperLimit *int64 `json:"winning_upper_limit" example:"10000"`
} }
// UpdateEventSettings godoc // UpdateEventSettings godoc
@ -534,8 +905,72 @@ type UpdateEventSettingsReq struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/tenant/{tenant_slug}/events/{id}/settings [put] // @Router /api/v1/events/{id}/settings [put]
func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error { func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
eventIDStr := c.Params("id")
eventID, err := strconv.ParseInt(eventIDStr, 10, 64)
if err != nil {
h.BadRequestLogger().Error("invalid event id")
return fiber.NewError(fiber.StatusBadRequest, "invalid event id")
}
var req UpdateEventSettingsReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("Failed to parse event id",
zap.Int64("eventID", eventID),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
logFields := []zap.Field{
zap.Int64("eventID", eventID),
zap.Any("is_featured", req.Featured),
zap.Any("is_active", req.IsActive),
zap.Any("winning_upper_limit", req.WinningUpperLimit),
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Error("Failed to update event settings",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err = h.eventSvc.UpdateGlobalEventSettings(c.Context(), domain.UpdateGlobalEventSettings{
EventID: eventID,
IsFeatured: domain.ConvertBoolPtr(req.Featured),
IsActive: domain.ConvertBoolPtr(req.IsActive),
WinningUpperLimit: domain.ConvertInt64Ptr(req.WinningUpperLimit),
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update event settings", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil)
}
// UpdateTenantEventSettings godoc
// @Summary update the event settings
// @Description Update the event settings
// @Tags event
// @Accept json
// @Produce json
// @Param id path int true "Event ID"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/tenant/{tenant_slug}/events/{id}/settings [put]
func (h *Handler) UpdateTenantEventSettings(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64) companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid { if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id") h.BadRequestLogger().Error("invalid company id")
@ -579,12 +1014,12 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
err = h.eventSvc.UpdateEventSettings(c.Context(), domain.CreateEventSettings{ err = h.eventSvc.UpdateTenantEventSettings(c.Context(), domain.UpdateTenantEventSettings{
CompanyID: companyID.Value, CompanyID: companyID.Value,
EventID: eventID, EventID: eventID,
IsFeatured: domain.ConvertBoolPtr(req.Featured), IsFeatured: domain.ConvertBoolPtr(req.Featured),
IsActive: domain.ConvertBoolPtr(req.IsActive), IsActive: domain.ConvertBoolPtr(req.IsActive),
WinningUpperLimit: domain.ConvertIntPtr(req.WinningUpperLimit), WinningUpperLimit: domain.ConvertInt64Ptr(req.WinningUpperLimit),
}) })
if err != nil { if err != nil {

View File

@ -26,7 +26,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
limit := domain.ValidInt64{ limit := domain.ValidInt64{
Value: int64(pageSize), Value: int64(pageSize),
Valid: pageSize == 0, Valid: pageSize != 0,
} }
offset := domain.ValidInt64{ offset := domain.ValidInt64{
Value: int64(page - 1), Value: int64(page - 1),
@ -54,7 +54,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
sportIDQuery := c.Query("sport_id") sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32 var sportID domain.ValidInt32
if sportIDQuery != "" { if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery) sportIDint, err := strconv.ParseInt(sportIDQuery, 10, 64)
if err != nil { if err != nil {
h.BadRequestLogger().Info("invalid sport id", h.BadRequestLogger().Info("invalid sport id",
zap.String("sport_id", sportIDQuery), zap.String("sport_id", sportIDQuery),
@ -86,7 +86,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
zap.Bool("sport_id_valid", sportID.Valid), zap.Bool("sport_id_valid", sportID.Valid),
} }
leagues, err := h.leagueSvc.GetAllLeagues(c.Context(), domain.LeagueFilter{ leagues, total, err := h.leagueSvc.GetAllLeagues(c.Context(), domain.LeagueFilter{
CountryCode: countryCode, CountryCode: countryCode,
IsActive: isActive, IsActive: isActive,
SportID: sportID, SportID: sportID,
@ -104,7 +104,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
res := domain.ConvertBaseLeagueResList(leagues) res := domain.ConvertBaseLeagueResList(leagues)
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", res, nil) return response.WritePaginatedJSON(c, fiber.StatusOK, "All leagues retrieved successfully", res, nil, page, int(total))
} }
// GetAllLeaguesForTenant godoc // GetAllLeaguesForTenant godoc
@ -156,7 +156,7 @@ func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
sportIDQuery := c.Query("sport_id") sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32 var sportID domain.ValidInt32
if sportIDQuery != "" { if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery) sportIDint, err := strconv.ParseInt(sportIDQuery, 10, 64)
if err != nil { if err != nil {
h.BadRequestLogger().Info("invalid sport id", h.BadRequestLogger().Info("invalid sport id",
zap.String("sport_id", sportIDQuery), zap.String("sport_id", sportIDQuery),
@ -235,7 +235,7 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
if leagueIdStr == "" { if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id") return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
} }
leagueId, err := strconv.Atoi(leagueIdStr) leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64)
if err != nil { if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid league id") return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
} }
@ -266,7 +266,7 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
} }
if err := h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{ if err := h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{
LeagueID: int64(leagueId), LeagueID: leagueId,
CompanyID: companyID.Value, CompanyID: companyID.Value,
IsActive: domain.ValidBool{ IsActive: domain.ValidBool{
Value: req.IsActive, Value: req.IsActive,
@ -308,7 +308,7 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
if leagueIdStr == "" { if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id") return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
} }
leagueId, err := strconv.Atoi(leagueIdStr) leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64)
if err != nil { if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid league id") return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
} }
@ -336,7 +336,7 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
} }
err = h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{ err = h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{
LeagueID: int64(leagueId), LeagueID: leagueId,
CompanyID: companyID.Value, CompanyID: companyID.Value,
IsFeatured: domain.ValidBool{ IsFeatured: domain.ValidBool{
Value: req.IsFeatured, Value: req.IsFeatured,
@ -351,3 +351,45 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
h.SuccessResLogger().Info("League Featured has been successfully updated", queryLogFields...) h.SuccessResLogger().Info("League Featured has been successfully updated", queryLogFields...)
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil) return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
} }
func (h *Handler) UpdateGlobalLeagueSetting(c *fiber.Ctx) error {
leagueIdStr := c.Params("id")
if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
}
leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
}
var req domain.UpdateLeagueSettingsReq
if err := c.BodyParser(&req); err != nil {
h.BadRequestLogger().Info("UpdateLeagueSettingsReq failed to parse request body", zap.String("league_id", leagueIdStr), zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request body:"+err.Error())
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.BadRequestLogger().Info("Failed to validate UpdateLeagueSettingsReq", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err = h.leagueSvc.UpdateGlobalLeagueSettings(c.Context(), domain.UpdateGlobalLeagueSettings{
ID: leagueId,
DefaultIsActive: domain.ConvertBoolPtr(req.IsActive),
DefaultIsFeatured: domain.ConvertBoolPtr(req.IsFeatured),
})
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update league", zap.Error(err), zap.String("leagueId", leagueIdStr))
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
}

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