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/institutions"
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/messenger"
notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification"
@ -122,10 +121,10 @@ func main() {
// var userStore user.UserStore
// Initialize producer
topic := "wallet-balance-topic"
producer := kafka.NewProducer(cfg.KafkaBrokers, topic)
// topic := "wallet-balance-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(
wallet.WalletStore(store),
@ -135,7 +134,6 @@ func main() {
userSvc,
domain.MongoDBLogger,
logger,
producer,
)
branchSvc := branch.NewService(store)
@ -195,8 +193,8 @@ func main() {
store,
)
go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, veliVirtualGameService, "C:/Users/User/Desktop")
go httpserver.StartEnetPulseCron(enePulseSvc, domain.MongoDBLogger)
go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, veliVirtualGameService, "C:/Users/User/Desktop")
go httpserver.ProcessBetCashback(context.TODO(), betSvc)
bankRepository := repository.NewBankRepository(store)
@ -233,13 +231,13 @@ func main() {
fixerFertcherSvc,
)
exchangeWorker := currency.NewExchangeRateWorker(fixerFertcherSvc, logger, cfg)
exchangeWorker.Start(context.Background())
defer exchangeWorker.Stop()
// exchangeWorker := currency.NewExchangeRateWorker(fixerFertcherSvc, logger, cfg)
// exchangeWorker.Start(context.Background())
// defer exchangeWorker.Stop()
go walletMonitorSvc.Start()
httpserver.StartDataFetchingCrons(eventSvc, *oddsSvc, resultSvc, domain.MongoDBLogger)
httpserver.StartTicketCrons(*ticketSvc, domain.MongoDBLogger)
httpserver.StartCleanupCrons(*ticketSvc, notificationSvc, domain.MongoDBLogger)
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,
created_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 (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
@ -186,19 +185,19 @@ CREATE TABLE IF NOT EXISTS banks (
currency VARCHAR(10) NOT NULL,
bank_logo TEXT -- URL or base64 string
);
CREATE TABLE IF NOT EXISTS wallets (
id BIGSERIAL PRIMARY KEY,
balance BIGINT NOT NULL DEFAULT 0,
is_withdraw BOOLEAN NOT NULL,
is_bettable BOOLEAN NOT NULL,
is_transferable BOOLEAN NOT NULL,
user_id BIGINT NOT NULL,
type VARCHAR(255) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT balance_positve CHECK (balance >= 0)
);
-- CREATE TABLE IF NOT EXISTS wallets (
-- id BIGSERIAL PRIMARY KEY,
-- balance BIGINT NOT NULL DEFAULT 0,
-- is_withdraw BOOLEAN NOT NULL,
-- is_bettable BOOLEAN NOT NULL,
-- is_transferable BOOLEAN NOT NULL,
-- user_id BIGINT NOT NULL,
-- type VARCHAR(255) NOT NULL,
-- is_active BOOLEAN NOT NULL DEFAULT true,
-- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- CONSTRAINT balance_positve CHECK (balance >= 0)
-- );
CREATE TABLE IF NOT EXISTS customer_wallets (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL,
@ -272,7 +271,7 @@ CREATE TABLE IF NOT EXISTS branches (
name VARCHAR(255) NOT NULL,
location TEXT 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,
branch_manager_id BIGINT NOT NULL,
company_id BIGINT NOT NULL,
@ -321,6 +320,7 @@ CREATE TABLE events (
is_live BOOLEAN NOT NULL DEFAULT false,
status TEXT NOT NULL,
fetched_at TIMESTAMP DEFAULT now (),
updated_at TIMESTAMP DEFAULT now (),
source TEXT NOT NULL DEFAULT 'b365api' CHECK (
source IN ('b365api', 'bfair', '1xbet', 'bwin', 'enetpulse')
),
@ -342,7 +342,7 @@ CREATE TABLE company_event_settings (
event_id BIGINT NOT NULL,
is_active BOOLEAN,
is_featured BOOLEAN,
winning_upper_limit INT,
winning_upper_limit BIGINT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (company_id, event_id)
);
@ -354,6 +354,7 @@ CREATE TABLE odds_market (
market_category TEXT NOT NULL,
market_id BIGINT NOT NULL,
raw_odds JSONB NOT NULL,
number_of_outcomes BIGINT NOT NULL,
default_is_active BOOLEAN NOT NULL DEFAULT true,
fetched_at TIMESTAMP DEFAULT now (),
expires_at TIMESTAMP NOT NULL,
@ -408,11 +409,11 @@ CREATE TABLE companies (
admin_id BIGINT NOT NULL,
wallet_id BIGINT 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,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT deducted_percentage_check CHECK (
deducted_percentage >= 0
deducted_percentage > 0
AND deducted_percentage < 1
)
);
@ -511,6 +512,8 @@ CREATE TABLE IF NOT EXISTS raffles (
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
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')),
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'completed'))
);
@ -580,14 +583,17 @@ CREATE VIEW bet_with_outcomes AS
SELECT bets.*,
CONCAT (users.first_name, ' ', users.last_name) AS full_name,
users.phone_number,
JSON_AGG (bet_outcomes.*) AS outcomes
JSON_AGG (bet_outcomes.*) AS outcomes,
companies.slug as company_slug
FROM bets
LEFT JOIN bet_outcomes ON bets.id = bet_outcomes.bet_id
LEFT JOIN users ON bets.user_id = users.id
JOIN companies ON bets.company_id = companies.id
GROUP BY bets.id,
users.first_name,
users.last_name,
users.phone_number;
users.phone_number,
companies.slug;
CREATE VIEW ticket_with_outcomes AS
SELECT tickets.*,
JSON_AGG (ticket_outcomes.*) AS outcomes
@ -644,6 +650,7 @@ SELECT sb.*,
st.verified AS transaction_verified,
bets.status,
bets.total_odds,
bets.fast_code,
JSON_AGG (bet_outcomes.*) AS outcomes
FROM shop_bets AS sb
JOIN shop_transactions st ON st.id = sb.shop_transaction_id
@ -657,7 +664,8 @@ GROUP BY sb.id,
st.amount,
st.verified,
bets.status,
bets.total_odds;
bets.total_odds,
bets.fast_code;
CREATE VIEW shop_deposit_detail AS
SELECT sd.*,
st.full_name,
@ -685,16 +693,30 @@ SELECT e.*,
ces.winning_upper_limit,
e.default_winning_upper_limit
) AS winning_upper_limit,
ces.updated_at,
l.country_code as league_cc
ces.updated_at as company_updated_at,
l.country_code as league_cc,
COALESCE(om.total_outcomes, 0) AS total_outcomes
FROM events e
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
SELECT events.*,
leagues.country_code as league_cc
leagues.country_code as league_cc,
COALESCE(om.total_outcomes, 0) AS total_outcomes
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
SELECT o.id,
o.event_id,
@ -702,6 +724,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,

View File

@ -37,6 +37,8 @@ CREATE TABLE IF NOT EXISTS notifications (
priority INTEGER,
version INTEGER NOT NULL DEFAULT 0,
timestamp TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
img TEXT,
expires TIMESTAMPTZ NOT NULL,
metadata JSONB
);
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_tournaments;
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
WHERE status = 2
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
SELECT *
FROM bet_outcomes
@ -180,6 +214,15 @@ UPDATE bet_outcomes
SEt status = $1
WHERE event_id = $2
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
UPDATE bets
SET status = $1,

View File

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

View File

@ -65,7 +65,11 @@ WHERE branch_manager_id = $1;
-- name: SearchBranchByName :many
SELECT *
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
SELECT *
FROM supported_operations;

View File

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

View File

@ -107,7 +107,8 @@ INSERT INTO enetpulse_tournament_stages (
updates_count,
last_updated_at,
status
) VALUES (
)
VALUES (
$1, -- stage_id
$2, -- name
$3, -- tournament_fk
@ -120,6 +121,19 @@ INSERT INTO enetpulse_tournament_stages (
$10, -- last_updated_at
$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 *;
-- name: GetAllEnetpulseTournamentStages :many
@ -367,6 +381,48 @@ SELECT *
FROM enetpulse_preodds_bettingoffers
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,
default_winning_upper_limit = EXCLUDED.default_winning_upper_limit,
fetched_at = now();
-- name: SaveEventSettings :exec
-- name: SaveTenantEventSettings :exec
INSERT INTO company_event_settings (
company_id,
event_id,
@ -218,11 +218,18 @@ SELECT e.*,
e.default_winning_upper_limit
) AS winning_upper_limit,
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
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $1
JOIN leagues l ON l.id = e.league_id
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 (
is_live = sqlc.narg('is_live')
OR sqlc.narg('is_live') IS NULL
@ -292,15 +299,24 @@ SELECT e.*,
e.default_winning_upper_limit
) AS winning_upper_limit,
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
LEFT JOIN company_event_settings ces ON e.id = ces.event_id
AND ces.company_id = $2
JOIN leagues l ON l.id = e.league_id
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
LIMIT 1;
-- name: GetSportAndLeagueIDs :one
SELECT sport_id, league_id FROM events
SELECT sport_id,
league_id
FROM events
WHERE id = $1;
-- name: UpdateMatchResult :exec
UPDATE events
@ -313,8 +329,22 @@ FROM events
WHERE id = $1;
-- name: UpdateEventMonitored :exec
UPDATE events
SET is_monitored = $1
SET is_monitored = $1,
updated_at = CURRENT_TIMESTAMP
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
DELETE FROM events
WHERE id = $1;
WHERE id = $1;

View File

@ -14,7 +14,7 @@ SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id,
sport_id = EXCLUDED.sport_id;
-- name: InsertLeagueSettings :exec
-- name: SaveLeagueSettings :exec
INSERT INTO company_league_settings (
company_id,
league_id,
@ -40,8 +40,31 @@ WHERE (
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
)
ORDER BY name ASC
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
SELECT COUNT(*)
FROM leagues l
@ -118,7 +141,7 @@ SET name = COALESCE(sqlc.narg('name'), name),
bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id),
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
WHERE id = $1;
-- name: UpdateLeagueSettings :exec
-- name: UpdateCompanyLeagueSettings :exec
UPDATE company_league_settings
SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured = COALESCE(
@ -126,4 +149,9 @@ SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured
)
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,
priority,
timestamp,
expires,
img,
metadata
)
VALUES (
@ -27,7 +29,9 @@ VALUES (
$10,
$11,
$12,
$13
$13,
$14,
$15
)
RETURNING *;
-- name: GetNotification :one
@ -88,4 +92,8 @@ SELECT COUNT(*) as total,
WHEN is_read = false THEN 1
END
) 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_category,
market_id,
number_of_outcomes,
raw_odds,
fetched_at,
expires_at
@ -17,13 +18,15 @@ VALUES (
$5,
$6,
$7,
$8
$8,
$9
) ON CONFLICT (event_id, market_id) DO
UPDATE
SET market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category,
raw_odds = EXCLUDED.raw_odds,
number_of_outcomes = EXCLUDED.number_of_outcomes,
fetched_at = EXCLUDED.fetched_at,
expires_at = EXCLUDED.expires_at;
-- name: SaveOddSettings :exec
@ -48,6 +51,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,
@ -75,6 +79,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,
@ -94,6 +99,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,
@ -129,6 +135,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,
@ -143,4 +150,15 @@ WHERE event_id = $1
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: DeleteOddsForEvent :exec
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
INSERT INTO raffles (company_id, name, expires_at, type)
VALUES ($1, $2, $3, $4)
INSERT INTO raffles (company_id, name, expires_at, ticket_limit, type)
VALUES ($1, $2, $3, $4, $5)
RETURNING *;
-- name: GetRafflesOfCompany :many
@ -71,3 +71,19 @@ FROM raffle_sport_filters
WHERE raffle_id = $1
AND sport_id = $2
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
restart: always
ports:
- "27017:27017"
- "27022:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: secret
@ -56,47 +56,17 @@ services:
]
networks:
- app
redis:
image: redis:7-alpine
ports:
- "6379:6379"
networks:
- app
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
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
# redis:
# image: redis:7-alpine
# ports:
# - "6379:6379"
# networks:
# - app
# healthcheck:
# test: ["CMD", "redis-cli", "ping"]
# interval: 10s
# timeout: 5s
# retries: 5
app:
build:
context: .
@ -107,15 +77,9 @@ services:
environment:
- DB_URL=postgresql://root:secret@postgres:5432/gh?sslmode=disable
- MONGO_URI=mongodb://root:secret@mongo:27017
- REDIS_ADDR=redis:6379
- KAFKA_BROKERS=kafka:9092
depends_on:
migrate:
condition: service_completed_successfully
mongo:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app
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"
)
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
INSERT INTO bets (
amount,
@ -104,7 +120,7 @@ func (q *Queries) DeleteBetOutcome(ctx context.Context, betID int64) error {
}
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
wHERE (
user_id = $1
@ -192,6 +208,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
&i.CompanySlug,
); err != nil {
return nil, err
}
@ -204,7 +221,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
}
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
WHERE fast_code = $1
LIMIT 1
@ -230,12 +247,13 @@ func (q *Queries) GetBetByFastCode(ctx context.Context, fastCode string) (BetWit
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
&i.CompanySlug,
)
return i, err
}
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
WHERE id = $1
`
@ -260,12 +278,13 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
&i.CompanySlug,
)
return i, err
}
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
WHERE user_id = $1
`
@ -296,6 +315,7 @@ func (q *Queries) GetBetByUserID(ctx context.Context, userID int64) ([]BetWithOu
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
&i.CompanySlug,
); err != nil {
return nil, err
}
@ -448,8 +468,109 @@ func (q *Queries) GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (i
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
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
WHERE status = 2
AND processed = false
@ -481,6 +602,7 @@ func (q *Queries) GetBetsForCashback(ctx context.Context) ([]BetWithOutcome, err
&i.FullName,
&i.PhoneNumber,
&i.Outcomes,
&i.CompanySlug,
); err != nil {
return nil, err
}
@ -557,6 +679,34 @@ func (q *Queries) GetTotalBets(ctx context.Context, arg GetTotalBetsParams) (int
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
UPDATE bet_outcomes
SET status = $1
@ -675,6 +825,54 @@ func (q *Queries) UpdateBetOutcomeStatusForEvent(ctx context.Context, arg Update
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
UPDATE bets
SET processed = $1

View File

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

View File

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

View File

@ -643,7 +643,8 @@ INSERT INTO enetpulse_tournament_stages (
updates_count,
last_updated_at,
status
) VALUES (
)
VALUES (
$1, -- stage_id
$2, -- name
$3, -- tournament_fk
@ -656,6 +657,19 @@ INSERT INTO enetpulse_tournament_stages (
$10, -- last_updated_at
$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
`
@ -1158,6 +1172,134 @@ func (q *Queries) GetAllEnetpulseTournaments(ctx context.Context) ([]EnetpulseTo
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
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

View File

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

View File

@ -48,14 +48,19 @@ WHERE (
name ILIKE '%' || $3 || '%'
OR $3 IS NULL
)
AND (
default_is_active = $4
OR $4 IS NULL
)
ORDER BY name ASC
LIMIT $5 OFFSET $4
LIMIT $6 OFFSET $5
`
type GetAllLeaguesParams struct {
CountryCode pgtype.Text `json:"country_code"`
SportID pgtype.Int4 `json:"sport_id"`
Query pgtype.Text `json:"query"`
IsActive pgtype.Bool `json:"is_active"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
@ -65,6 +70,7 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
arg.CountryCode,
arg.SportID,
arg.Query,
arg.IsActive,
arg.Offset,
arg.Limit,
)
@ -199,6 +205,46 @@ func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeagu
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
SELECT COUNT(*)
FROM leagues l
@ -292,7 +338,7 @@ func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) erro
return err
}
const InsertLeagueSettings = `-- name: InsertLeagueSettings :exec
const SaveLeagueSettings = `-- name: SaveLeagueSettings :exec
INSERT INTO company_league_settings (
company_id,
league_id,
@ -305,15 +351,15 @@ SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured
`
type InsertLeagueSettingsParams struct {
type SaveLeagueSettingsParams struct {
CompanyID int64 `json:"company_id"`
LeagueID int64 `json:"league_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
}
func (q *Queries) InsertLeagueSettings(ctx context.Context, arg InsertLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, InsertLeagueSettings,
func (q *Queries) SaveLeagueSettings(ctx context.Context, arg SaveLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, SaveLeagueSettings,
arg.CompanyID,
arg.LeagueID,
arg.IsActive,
@ -322,6 +368,52 @@ func (q *Queries) InsertLeagueSettings(ctx context.Context, arg InsertLeagueSett
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
UPDATE leagues
SET name = COALESCE($2, name),
@ -349,31 +441,3 @@ func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) erro
)
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"`
PhoneNumber pgtype.Text `json:"phone_number"`
Outcomes []BetOutcome `json:"outcomes"`
CompanySlug string `json:"company_slug"`
}
type Branch struct {
@ -177,7 +178,7 @@ type CompanyEventSetting struct {
EventID int64 `json:"event_id"`
IsActive pgtype.Bool `json:"is_active"`
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"`
}
@ -468,6 +469,7 @@ type Event struct {
IsLive bool `json:"is_live"`
Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
@ -504,12 +506,14 @@ type EventWithCountry struct {
IsLive bool `json:"is_live"`
Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"`
LeagueCc pgtype.Text `json:"league_cc"`
TotalOutcomes int64 `json:"total_outcomes"`
}
type EventWithSetting struct {
@ -534,6 +538,7 @@ type EventWithSetting struct {
IsLive bool `json:"is_live"`
Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
@ -542,9 +547,10 @@ type EventWithSetting struct {
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
WinningUpperLimit int64 `json:"winning_upper_limit"`
CompanyUpdatedAt pgtype.Timestamp `json:"company_updated_at"`
LeagueCc pgtype.Text `json:"league_cc"`
TotalOutcomes int64 `json:"total_outcomes"`
}
type ExchangeRate struct {
@ -619,6 +625,8 @@ type Notification struct {
Priority pgtype.Int4 `json:"priority"`
Version int32 `json:"version"`
Timestamp pgtype.Timestamptz `json:"timestamp"`
Img pgtype.Text `json:"img"`
Expires pgtype.Timestamptz `json:"expires"`
Metadata []byte `json:"metadata"`
}
@ -633,49 +641,52 @@ type OddHistory struct {
}
type OddsMarket struct {
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
RawOdds []byte `json:"raw_odds"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
RawOdds []byte `json:"raw_odds"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
}
type OddsMarketWithEvent struct {
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
RawOdds []byte `json:"raw_odds"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
IsMonitored bool `json:"is_monitored"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
Source string `json:"source"`
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
RawOdds []byte `json:"raw_odds"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
IsMonitored bool `json:"is_monitored"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
Source string `json:"source"`
}
type OddsMarketWithSetting struct {
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type Otp struct {
@ -691,13 +702,14 @@ type Otp struct {
}
type Raffle struct {
ID int32 `json:"id"`
CompanyID int32 `json:"company_id"`
Name string `json:"name"`
CreatedAt pgtype.Timestamp `json:"created_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
Type string `json:"type"`
Status string `json:"status"`
ID int32 `json:"id"`
CompanyID int32 `json:"company_id"`
Name string `json:"name"`
CreatedAt pgtype.Timestamp `json:"created_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
TicketLimit int32 `json:"ticket_limit"`
Type string `json:"type"`
Status string `json:"status"`
}
type RaffleGameFilter struct {
@ -824,6 +836,7 @@ type ShopBetDetail struct {
TransactionVerified bool `json:"transaction_verified"`
Status int32 `json:"status"`
TotalOdds float32 `json:"total_odds"`
FastCode string `json:"fast_code"`
Outcomes []BetOutcome `json:"outcomes"`
}

View File

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

View File

@ -11,6 +11,32 @@ import (
"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
DELETE FROM odds_market
Where event_id = $1
@ -22,7 +48,7 @@ func (q *Queries) DeleteOddsForEvent(ctx context.Context, eventID int64) error {
}
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
LIMIT $2 OFFSET $1
`
@ -49,6 +75,7 @@ func (q *Queries) GetAllOdds(ctx context.Context, arg GetAllOddsParams) ([]OddsM
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.NumberOfOutcomes,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -74,6 +101,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,
@ -94,19 +122,20 @@ type GetAllOddsWithSettingsParams struct {
}
type GetAllOddsWithSettingsRow struct {
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWithSettingsParams) ([]GetAllOddsWithSettingsRow, error) {
@ -125,6 +154,7 @@ func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWith
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.NumberOfOutcomes,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -144,7 +174,7 @@ func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWith
}
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
WHERE id = $1
`
@ -160,6 +190,7 @@ func (q *Queries) GetOddByID(ctx context.Context, id int64) (OddsMarketWithEvent
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.NumberOfOutcomes,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -172,7 +203,7 @@ func (q *Queries) GetOddByID(ctx context.Context, id int64) (OddsMarketWithEvent
}
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
WHERE event_id = $1
AND (
@ -223,6 +254,7 @@ func (q *Queries) GetOddsByEventID(ctx context.Context, arg GetOddsByEventIDPara
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.NumberOfOutcomes,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -242,7 +274,7 @@ func (q *Queries) GetOddsByEventID(ctx context.Context, arg GetOddsByEventIDPara
}
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
WHERE market_id = $1
AND event_id = $2
@ -264,6 +296,7 @@ func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDPa
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.NumberOfOutcomes,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -282,6 +315,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,
@ -304,19 +338,20 @@ type GetOddsWithSettingsByEventIDParams struct {
}
type GetOddsWithSettingsByEventIDRow struct {
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsWithSettingsByEventIDParams) ([]GetOddsWithSettingsByEventIDRow, error) {
@ -340,6 +375,7 @@ func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsW
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.NumberOfOutcomes,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -365,6 +401,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,
@ -384,19 +421,20 @@ type GetOddsWithSettingsByIDParams struct {
}
type GetOddsWithSettingsByIDRow struct {
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSettingsByIDParams) (GetOddsWithSettingsByIDRow, error) {
@ -409,6 +447,7 @@ func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSe
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.NumberOfOutcomes,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -427,6 +466,7 @@ SELECT o.id,
o.market_name,
o.market_category,
o.market_id,
o.number_of_outcomes,
o.default_is_active,
o.fetched_at,
o.expires_at,
@ -448,19 +488,20 @@ type GetOddsWithSettingsByMarketIDParams struct {
}
type GetOddsWithSettingsByMarketIDRow struct {
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
ID int64 `json:"id"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID pgtype.Int8 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (GetOddsWithSettingsByMarketIDRow, error) {
@ -473,6 +514,7 @@ func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOdds
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.NumberOfOutcomes,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -491,6 +533,7 @@ INSERT INTO odds_market (
market_name,
market_category,
market_id,
number_of_outcomes,
raw_odds,
fetched_at,
expires_at
@ -503,26 +546,29 @@ VALUES (
$5,
$6,
$7,
$8
$8,
$9
) ON CONFLICT (event_id, market_id) DO
UPDATE
SET market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category,
raw_odds = EXCLUDED.raw_odds,
number_of_outcomes = EXCLUDED.number_of_outcomes,
fetched_at = EXCLUDED.fetched_at,
expires_at = EXCLUDED.expires_at
`
type InsertOddsMarketParams struct {
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
RawOdds []byte `json:"raw_odds"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
EventID int64 `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID int64 `json:"market_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
RawOdds []byte `json:"raw_odds"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
}
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.MarketCategory,
arg.MarketID,
arg.NumberOfOutcomes,
arg.RawOdds,
arg.FetchedAt,
arg.ExpiresAt,
@ -568,3 +615,19 @@ func (q *Queries) SaveOddSettings(ctx context.Context, arg SaveOddSettingsParams
)
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
}
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
SELECT COUNT(*) > 0 AS exists
FROM raffle_sport_filters
@ -57,16 +70,17 @@ func (q *Queries) CheckValidSportRaffleFilter(ctx context.Context, arg CheckVali
}
const CreateRaffle = `-- name: CreateRaffle :one
INSERT INTO raffles (company_id, name, expires_at, type)
VALUES ($1, $2, $3, $4)
RETURNING id, company_id, name, created_at, expires_at, type, status
INSERT INTO raffles (company_id, name, expires_at, ticket_limit, type)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, company_id, name, created_at, expires_at, ticket_limit, type, status
`
type CreateRaffleParams struct {
CompanyID int32 `json:"company_id"`
Name string `json:"name"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
Type string `json:"type"`
CompanyID int32 `json:"company_id"`
Name string `json:"name"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
TicketLimit int32 `json:"ticket_limit"`
Type string `json:"type"`
}
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.Name,
arg.ExpiresAt,
arg.TicketLimit,
arg.Type,
)
var i Raffle
@ -83,6 +98,7 @@ func (q *Queries) CreateRaffle(ctx context.Context, arg CreateRaffleParams) (Raf
&i.Name,
&i.CreatedAt,
&i.ExpiresAt,
&i.TicketLimit,
&i.Type,
&i.Status,
)
@ -140,7 +156,7 @@ func (q *Queries) CreateRaffleWinner(ctx context.Context, arg CreateRaffleWinner
const DeleteRaffle = `-- name: DeleteRaffle :one
DELETE FROM raffles
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) {
@ -152,6 +168,7 @@ func (q *Queries) DeleteRaffle(ctx context.Context, id int32) (Raffle, error) {
&i.Name,
&i.CreatedAt,
&i.ExpiresAt,
&i.TicketLimit,
&i.Type,
&i.Status,
)
@ -219,8 +236,40 @@ func (q *Queries) GetRaffleStanding(ctx context.Context, arg GetRaffleStandingPa
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
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) {
@ -238,6 +287,7 @@ func (q *Queries) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]R
&i.Name,
&i.CreatedAt,
&i.ExpiresAt,
&i.TicketLimit,
&i.Type,
&i.Status,
); err != nil {

View File

@ -173,7 +173,7 @@ func (q *Queries) CreateShopTransaction(ctx context.Context, arg CreateShopTrans
}
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
WHERE (
full_name ILIKE '%' || $1 || '%'
@ -239,6 +239,7 @@ func (q *Queries) GetAllShopBets(ctx context.Context, arg GetAllShopBetsParams)
&i.TransactionVerified,
&i.Status,
&i.TotalOdds,
&i.FastCode,
&i.Outcomes,
); err != nil {
return nil, err
@ -419,7 +420,7 @@ func (q *Queries) GetAllShopTransactions(ctx context.Context, arg GetAllShopTran
}
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
WHERE bet_id = $1
`
@ -445,13 +446,14 @@ func (q *Queries) GetShopBetByBetID(ctx context.Context, betID int64) (ShopBetDe
&i.TransactionVerified,
&i.Status,
&i.TotalOdds,
&i.FastCode,
&i.Outcomes,
)
return i, err
}
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
WHERE cashout_id = $1
`
@ -477,13 +479,14 @@ func (q *Queries) GetShopBetByCashoutID(ctx context.Context, cashoutID string) (
&i.TransactionVerified,
&i.Status,
&i.TotalOdds,
&i.FastCode,
&i.Outcomes,
)
return i, err
}
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
WHERE id = $1
`
@ -509,13 +512,14 @@ func (q *Queries) GetShopBetByID(ctx context.Context, id int64) (ShopBetDetail,
&i.TransactionVerified,
&i.Status,
&i.TotalOdds,
&i.FastCode,
&i.Outcomes,
)
return i, err
}
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
WHERE shop_transaction_id = $1
`
@ -541,6 +545,7 @@ func (q *Queries) GetShopBetByShopTransactionID(ctx context.Context, shopTransac
&i.TransactionVerified,
&i.Status,
&i.TotalOdds,
&i.FastCode,
&i.Outcomes,
)
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/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/pkg/errors v0.9.1 // indirect
github.com/redis/go-redis/v9 v9.10.0 // direct
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 // 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/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
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/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
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/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
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/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/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
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/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
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/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.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
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/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/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
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/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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/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/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ=
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/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
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/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=
@ -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-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.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
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/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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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/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-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.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/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-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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
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=
@ -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-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.5.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/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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.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.3/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.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/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
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.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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -90,6 +90,7 @@ type GetBet struct {
PhoneNumber string
UserID int64
CompanyID int64
CompanySlug string
IsShopBet bool
CashedOut bool
Outcomes []BetOutcome
@ -149,18 +150,49 @@ type CreateBetRes struct {
FastCode string `json:"fast_code"`
}
type BetRes struct {
ID int64 `json:"id" example:"1"`
Outcomes []BetOutcome `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"`
Status OutcomeStatus `json:"status" example:"1"`
Fullname string `json:"full_name" example:"John Smith"`
UserID int64 `json:"user_id" example:"2"`
CompanyID int64 `json:"company_id" example:"1"`
IsShopBet bool `json:"is_shop_bet" example:"false"`
CashedOut bool `json:"cashed_out" example:"false"`
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
FastCode string `json:"fast_code"`
ID int64 `json:"id" example:"1"`
Outcomes []BetOutcome `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"`
Status OutcomeStatus `json:"status" example:"1"`
Fullname string `json:"full_name" example:"John Smith"`
UserID int64 `json:"user_id" example:"2"`
CompanyID int64 `json:"company_id" example:"1"`
CompanySlug string `json:"company_slug" example:"fortune"`
IsShopBet bool `json:"is_shop_bet" example:"false"`
CashedOut bool `json:"cashed_out" example:"false"`
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 {
@ -179,18 +211,19 @@ func ConvertCreateBetRes(bet Bet, createdNumber int64) CreateBetRes {
func ConvertBet(bet GetBet) BetRes {
return BetRes{
ID: bet.ID,
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
Fullname: bet.FullName,
UserID: bet.UserID,
CompanyID: bet.CompanyID,
Outcomes: bet.Outcomes,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CreatedAt: bet.CreatedAt,
FastCode: bet.FastCode,
ID: bet.ID,
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
Fullname: bet.FullName,
UserID: bet.UserID,
CompanyID: bet.CompanyID,
CompanySlug: bet.CompanySlug,
Outcomes: bet.Outcomes,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CreatedAt: bet.CreatedAt,
FastCode: bet.FastCode,
}
}
@ -228,6 +261,30 @@ func ConvertDBBetOutcomes(outcome dbgen.BetOutcome) BetOutcome {
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 {
var outcomes []BetOutcome = make([]BetOutcome, 0, len(bet.Outcomes))
@ -239,6 +296,7 @@ func ConvertDBBetWithOutcomes(bet dbgen.BetWithOutcome) GetBet {
return GetBet{
ID: bet.ID,
CompanyID: bet.CompanyID,
CompanySlug: bet.CompanySlug,
Amount: Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: OutcomeStatus(bet.Status),

View File

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

View File

@ -25,49 +25,83 @@ func (m Currency) String() string {
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
const (
ETB IntCurrency = "ETB" // Ethiopian Birr
NGN IntCurrency = "NGN" // Nigerian Naira
ZAR IntCurrency = "ZAR" // South African Rand
EGP IntCurrency = "EGP" // Egyptian Pound
KES IntCurrency = "KES" // Kenyan Shilling
UGX IntCurrency = "UGX" // Ugandan Shilling
TZS IntCurrency = "TZS" // Tanzanian Shilling
RWF IntCurrency = "RWF" // Rwandan Franc
BIF IntCurrency = "BIF" // Burundian Franc
XOF IntCurrency = "XOF" // West African CFA Franc (BCEAO)
XAF IntCurrency = "XAF" // Central African CFA Franc (BEAC)
GHS IntCurrency = "GHS" // Ghanaian Cedi
SDG IntCurrency = "SDG" // Sudanese Pound
SSP IntCurrency = "SSP" // South Sudanese Pound
DZD IntCurrency = "DZD" // Algerian Dinar
MAD IntCurrency = "MAD" // Moroccan Dirham
TND IntCurrency = "TND" // Tunisian Dinar
LYD IntCurrency = "LYD" // Libyan Dinar
MZN IntCurrency = "MZN" // Mozambican Metical
AOA IntCurrency = "AOA" // Angolan Kwanza
BWP IntCurrency = "BWP" // Botswana Pula
ZMW IntCurrency = "ZMW" // Zambian Kwacha
MWK IntCurrency = "MWK" // Malawian Kwacha
LSL IntCurrency = "LSL" // Lesotho Loti
NAD IntCurrency = "NAD" // Namibian Dollar
SZL IntCurrency = "SZL" // Swazi Lilangeni
CVE IntCurrency = "CVE" // Cape Verdean Escudo
GMD IntCurrency = "GMD" // Gambian Dalasi
SLL IntCurrency = "SLL" // Sierra Leonean Leone
LRD IntCurrency = "LRD" // Liberian Dollar
GNF IntCurrency = "GNF" // Guinean Franc
XCD IntCurrency = "XCD" // Eastern Caribbean Dollar (used in Saint Lucia)
MRU IntCurrency = "MRU" // Mauritanian Ouguiya
KMF IntCurrency = "KMF" // Comorian Franc
DJF IntCurrency = "DJF" // Djiboutian Franc
SOS IntCurrency = "SOS" // Somali Shilling
ERN IntCurrency = "ERN" // Eritrean Nakfa
MGA IntCurrency = "MGA" // Malagasy Ariary
SCR IntCurrency = "SCR" // Seychellois Rupee
MUR IntCurrency = "MUR" // Mauritian Rupee
BASE IntCurrency = "BASE"
ETB IntCurrency = "ETB" // Ethiopian Birr
NGN IntCurrency = "NGN" // Nigerian Naira
ZAR IntCurrency = "ZAR" // South African Rand
EGP IntCurrency = "EGP" // Egyptian Pound
KES IntCurrency = "KES" // Kenyan Shilling
UGX IntCurrency = "UGX" // Ugandan Shilling
TZS IntCurrency = "TZS" // Tanzanian Shilling
RWF IntCurrency = "RWF" // Rwandan Franc
BIF IntCurrency = "BIF" // Burundian Franc
XOF IntCurrency = "XOF" // West African CFA Franc (BCEAO)
XAF IntCurrency = "XAF" // Central African CFA Franc (BEAC)
GHS IntCurrency = "GHS" // Ghanaian Cedi
SDG IntCurrency = "SDG" // Sudanese Pound
SSP IntCurrency = "SSP" // South Sudanese Pound
DZD IntCurrency = "DZD" // Algerian Dinar
MAD IntCurrency = "MAD" // Moroccan Dirham
TND IntCurrency = "TND" // Tunisian Dinar
LYD IntCurrency = "LYD" // Libyan Dinar
MZN IntCurrency = "MZN" // Mozambican Metical
AOA IntCurrency = "AOA" // Angolan Kwanza
BWP IntCurrency = "BWP" // Botswana Pula
ZMW IntCurrency = "ZMW" // Zambian Kwacha
MWK IntCurrency = "MWK" // Malawian Kwacha
LSL IntCurrency = "LSL" // Lesotho Loti
NAD IntCurrency = "NAD" // Namibian Dollar
SZL IntCurrency = "SZL" // Swazi Lilangeni
CVE IntCurrency = "CVE" // Cape Verdean Escudo
GMD IntCurrency = "GMD" // Gambian Dalasi
SLL IntCurrency = "SLL" // Sierra Leonean Leone
LRD IntCurrency = "LRD" // Liberian Dollar
GNF IntCurrency = "GNF" // Guinean Franc
XCD IntCurrency = "XCD" // Eastern Caribbean Dollar (used in Saint Lucia)
MRU IntCurrency = "MRU" // Mauritanian Ouguiya
KMF IntCurrency = "KMF" // Comorian Franc
DJF IntCurrency = "DJF" // Djiboutian Franc
SOS IntCurrency = "SOS" // Somali Shilling
ERN IntCurrency = "ERN" // Eritrean Nakfa
MGA IntCurrency = "MGA" // Malagasy Ariary
SCR IntCurrency = "SCR" // Seychellois Rupee
MUR IntCurrency = "MUR" // Mauritian Rupee
// International currencies (already listed)
USD IntCurrency = "USD" // US Dollar

View File

@ -486,25 +486,23 @@ type CreateEnetpulseFixture struct {
// Full domain model
type EnetpulseFixture struct {
FixtureID string
Name string
SportFK string
TournamentFK string
TournamentTemplateFK string
TournamentStageFK string
TournamentStageName string
TournamentName string
TournamentTemplateName string
SportName string
Gender string
StartDate time.Time
StatusType string
StatusDescFK string
RoundTypeFK string
UpdatesCount int
LastUpdatedAt time.Time
CreatedAt time.Time
UpdatedAt time.Time
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"` // ISO 8601
StatusType string `json:"status_type"`
StatusDescFK string `json:"status_descFK"`
RoundTypeFK string `json:"round_typeFK"`
UpdatesCount string `json:"n"` // convert to int
LastUpdatedAt string `json:"ut"` // parse to time.Time
}
type CreateEnetpulseResult struct {
@ -604,7 +602,7 @@ type CreateEnetpulseOutcomeType struct {
LastUpdatedAt time.Time `json:"last_updated_at"`
}
type EnetpulsePreodds struct {
type EnetpulsePreoddsTemp struct {
PreoddsID string `json:"preodds_id"`
EventFK string `json:"event_fk"`
OutcomeTypeFK string `json:"outcome_type_fk"`
@ -668,3 +666,41 @@ type EnetpulsePreoddsBettingOffer struct {
CreatedAt time.Time `json:"created_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
import (
"fmt"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
@ -50,6 +51,58 @@ const (
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 {
ID int64
SourceEventID string
@ -67,6 +120,7 @@ type BaseEvent struct {
StartTime time.Time
Source EventSource
Status EventStatus
TotalOddOutcomes int64
IsMonitored bool
DefaultIsFeatured bool
DefaultIsActive bool
@ -96,6 +150,7 @@ type BaseEventRes struct {
StartTime time.Time `json:"start_time"`
Source EventSource `json:"source"`
Status EventStatus `json:"status"`
TotalOddOutcomes int64 `json:"total_odd_outcomes"`
IsMonitored bool `json:"is_monitored"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"`
@ -125,10 +180,11 @@ type EventWithSettings struct {
StartTime time.Time
Source EventSource
Status EventStatus
TotalOddOutcomes int64
IsMonitored bool
IsFeatured bool
IsActive bool
WinningUpperLimit int32
WinningUpperLimit int64
DefaultIsFeatured bool
DefaultIsActive bool
DefaultWinningUpperLimit int64
@ -178,10 +234,11 @@ type EventWithSettingsRes struct {
StartTime time.Time `json:"start_time"`
Source EventSource `json:"source"`
Status EventStatus `json:"status"`
TotalOddOutcomes int64 `json:"total_odd_outcomes"`
IsMonitored bool `json:"is_monitored"`
IsFeatured bool `json:"is_featured"`
IsActive bool `json:"is_active"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
WinningUpperLimit int64 `json:"winning_upper_limit"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"`
DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"`
@ -204,12 +261,18 @@ type EventSettings struct {
UpdatedAt time.Time
}
type CreateEventSettings struct {
type UpdateTenantEventSettings struct {
CompanyID int64
EventID int64
IsActive ValidBool
IsFeatured ValidBool
WinningUpperLimit ValidInt
WinningUpperLimit ValidInt64
}
type UpdateGlobalEventSettings struct {
EventID int64
IsActive ValidBool
IsFeatured ValidBool
WinningUpperLimit ValidInt64
}
type ValidEventStatus struct {
@ -273,6 +336,7 @@ func ConvertDBEvent(event dbgen.EventWithCountry) BaseEvent {
StartTime: event.StartTime.Time.UTC(),
Source: EventSource(event.Source),
Status: EventStatus(event.Status),
TotalOddOutcomes: event.TotalOutcomes,
DefaultIsFeatured: event.DefaultIsFeatured,
IsMonitored: event.IsMonitored,
DefaultIsActive: event.DefaultIsActive,
@ -331,8 +395,8 @@ func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
}
}
func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.SaveEventSettingsParams {
return dbgen.SaveEventSettingsParams{
func ConvertCreateEventSettings(eventSettings UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams {
return dbgen.SaveTenantEventSettingsParams{
CompanyID: eventSettings.CompanyID,
EventID: eventSettings.EventID,
IsActive: eventSettings.IsActive.ToPG(),
@ -343,17 +407,19 @@ func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.SaveEve
func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings {
return EventWithSettings{
ID: event.ID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
AwayTeam: event.AwayTeam,
HomeTeamID: event.HomeTeamID,
AwayTeamID: event.AwayTeamID,
HomeTeamImage: event.HomeKitImage,
AwayTeamImage: event.AwayKitImage,
LeagueID: event.LeagueID,
LeagueName: event.LeagueName,
ID: event.ID,
SourceEventID: event.SourceEventID,
WinningUpperLimit: event.WinningUpperLimit,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
AwayTeam: event.AwayTeam,
HomeTeamID: event.HomeTeamID,
AwayTeamID: event.AwayTeamID,
HomeTeamImage: event.HomeKitImage,
AwayTeamImage: event.AwayKitImage,
LeagueID: event.LeagueID,
LeagueName: event.LeagueName,
LeagueCC: ValidString{
Value: event.LeagueCc.String,
Valid: event.LeagueCc.Valid,
@ -361,6 +427,7 @@ func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings {
StartTime: event.StartTime.Time.UTC(),
Source: EventSource(event.Source),
Status: EventStatus(event.Status),
TotalOddOutcomes: event.TotalOutcomes,
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
@ -401,8 +468,8 @@ func ConvertDBEventWithSettings(events []dbgen.EventWithSetting) []EventWithSett
return result
}
func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.SaveEventSettingsParams {
return dbgen.SaveEventSettingsParams{
func ConvertUpdateTenantEventSettings(event UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams {
return dbgen.SaveTenantEventSettingsParams{
EventID: event.EventID,
CompanyID: event.CompanyID,
IsActive: event.IsActive.ToPG(),
@ -410,10 +477,19 @@ func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.SaveEventSettin
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 {
return BaseEventRes{
ID: event.ID,
SourceEventID: event.SourceEventID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
@ -428,6 +504,7 @@ func ConvertEventRes(event BaseEvent) BaseEventRes {
StartTime: event.StartTime.UTC(),
Source: EventSource(event.Source),
Status: EventStatus(event.Status),
TotalOddOutcomes: event.TotalOddOutcomes,
DefaultIsFeatured: event.DefaultIsFeatured,
IsMonitored: event.IsMonitored,
DefaultIsActive: event.DefaultIsActive,
@ -452,6 +529,7 @@ func ConvertEventResList(events []BaseEvent) []BaseEventRes {
func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
return EventWithSettingsRes{
ID: event.ID,
SourceEventID: event.SourceEventID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
@ -466,6 +544,7 @@ func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
StartTime: event.StartTime.UTC(),
Source: EventSource(event.Source),
Status: EventStatus(event.Status),
TotalOddOutcomes: event.TotalOddOutcomes,
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
@ -480,6 +559,7 @@ func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
MatchPeriod: event.MatchPeriod.Value,
IsLive: event.IsLive,
FetchedAt: event.FetchedAt.UTC(),
UpdatedAt: event.UpdatedAt,
}
}

View File

@ -87,6 +87,17 @@ type UpdateLeague struct {
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 {
Query ValidString
CountryCode ValidString
@ -109,8 +120,8 @@ func ConvertCreateLeague(league CreateLeague) dbgen.InsertLeagueParams {
}
}
func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.InsertLeagueSettingsParams {
return dbgen.InsertLeagueSettingsParams{
func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.SaveLeagueSettingsParams {
return dbgen.SaveLeagueSettingsParams{
CompanyID: leagueSetting.CompanyID,
LeagueID: leagueSetting.LeagueID,
IsActive: leagueSetting.IsActive.ToPG(),
@ -149,7 +160,7 @@ func ConvertDBLeagueWithSetting(lws dbgen.GetAllLeaguesWithSettingsRow) LeagueWi
ID: lws.ID,
Name: lws.Name,
CompanyID: lws.CompanyID.Int64,
CountryCode: ValidString{
CountryCode: ValidString{
Value: lws.CountryCode.String,
Valid: lws.CountryCode.Valid,
},
@ -187,15 +198,15 @@ func ConvertUpdateLeague(updateLeague UpdateLeague) dbgen.UpdateLeagueParams {
func ConvertLeagueWithSettingRes(lws LeagueWithSettings) LeagueWithSettingsRes {
return LeagueWithSettingsRes{
ID: lws.ID,
Name: lws.Name,
CompanyID: lws.CompanyID,
CountryCode: lws.CountryCode.Value,
Bet365ID: lws.Bet365ID.Value,
IsActive: lws.IsActive,
SportID: lws.SportID,
IsFeatured: lws.IsFeatured,
UpdatedAt: lws.UpdatedAt,
ID: lws.ID,
Name: lws.Name,
CompanyID: lws.CompanyID,
CountryCode: lws.CountryCode.Value,
Bet365ID: lws.Bet365ID.Value,
IsActive: lws.IsActive,
SportID: lws.SportID,
IsFeatured: lws.IsFeatured,
UpdatedAt: lws.UpdatedAt,
DefaultIsActive: lws.DefaultIsActive,
DefaultIsFeatured: lws.DefaultIsFeatured,
}
@ -213,12 +224,12 @@ func ConvertLeagueWithSettingResList(leagues []LeagueWithSettings) []LeagueWithS
func ConvertBaseLeagueRes(league BaseLeague) BaseLeagueRes {
return BaseLeagueRes{
ID: league.ID,
Name: league.Name,
CountryCode: league.CountryCode.Value,
Bet365ID: league.Bet365ID.Value,
SportID: league.SportID,
DefaultIsActive: league.DefaultIsActive,
ID: league.ID,
Name: league.Name,
CountryCode: league.CountryCode.Value,
Bet365ID: league.Bet365ID.Value,
SportID: league.SportID,
DefaultIsActive: league.DefaultIsActive,
DefaultIsFeatured: league.DefaultIsFeatured,
}
}
@ -231,3 +242,11 @@ func ConvertBaseLeagueResList(leagues []BaseLeague) []BaseLeagueRes {
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"`
Version int `json:"-"`
Timestamp time.Time `json:"timestamp"`
Expires time.Time `json:"expires"`
Image string `json:"image"`
Metadata json.RawMessage `json:"metadata,omitempty"`
}
type CreateNotification struct {
@ -97,6 +99,8 @@ type CreateNotification struct {
DeliveryChannel DeliveryChannel `json:"delivery_channel,omitempty"`
Payload NotificationPayload `json:"payload"`
Priority int `json:"priority,omitempty"`
Expires time.Time `json:"expires"`
Image string `json:"image,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty"`
}

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package domain
import (
"fmt"
"time"
"github.com/jackc/pgx/v5/pgtype"
@ -48,6 +49,28 @@ const (
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 {
switch *o {
case OUTCOME_STATUS_PENDING:
@ -72,7 +95,6 @@ type ValidOutcomeStatus struct {
Valid bool
}
func (v ValidOutcomeStatus) ToPG() pgtype.Int4 {
return pgtype.Int4{
Int32: int32(v.Value),
@ -80,7 +102,6 @@ func (v ValidOutcomeStatus) ToPG() pgtype.Int4 {
}
}
type TimeStatus int32
const (

View File

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

View File

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

View File

@ -103,8 +103,11 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma
Query: filter.Query.ToPG(),
CreatedBefore: filter.CreatedBefore.ToPG(),
CreatedAfter: filter.CreatedAfter.ToPG(),
Offset: filter.Offset.ToPG(),
Limit: filter.Limit.ToPG(),
Offset: pgtype.Int4{
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
Limit: filter.Limit.ToPG(),
})
if err != nil {
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(),
CreatedBefore: filter.CreatedBefore.ToPG(),
CreatedAfter: filter.CreatedAfter.ToPG(),
});
})
var result []domain.GetBet = make([]domain.GetBet, 0, len(bets))
for _, bet := range bets {
@ -275,6 +278,36 @@ func (s *Store) SettleWinningBet(ctx context.Context, betID int64, userID int64,
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) {
outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, dbgen.GetBetOutcomeByEventIDParams{
@ -377,6 +410,45 @@ func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int6
}
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 {
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
}
func (s *Store) SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error) {
dbBranches, err := s.queries.SearchBranchByName(ctx, pgtype.Text{String: name, Valid: true})
func (s *Store) SearchBranchByName(ctx context.Context, name string, companyID domain.ValidInt64) ([]domain.BranchDetail, error) {
dbBranches, err := s.queries.SearchBranchByName(ctx, dbgen.SearchBranchByNameParams{
Column1: pgtype.Text{String: name, Valid: true},
CompanyID: companyID.ToPG(),
})
if err != nil {
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 (
"context"
"errors"
"fmt"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) {
baseSlug := helpers.GenerateSlug(company.Name)
uniqueSlug := baseSlug
i := 1
// baseSlug := helpers.GenerateSlug(company.Name)
// uniqueSlug := baseSlug
// i := 1
for {
_, err := s.queries.GetCompanyIDUsingSlug(ctx, uniqueSlug)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
// slug is unique
break
} else {
// real DB error
return domain.Company{}, err
}
}
uniqueSlug = fmt.Sprintf("%s-%d", baseSlug, i)
i++
}
// for {
// _, err := s.queries.GetCompanyUsingSlug(ctx, uniqueSlug)
// if err != nil {
// if errors.Is(err, pgx.ErrNoRows) {
// // slug is unique
// break
// } else {
// // real DB error
// return domain.Company{}, err
// }
// }
// uniqueSlug = fmt.Sprintf("%s-%d", baseSlug, 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 {
return domain.Company{}, err
}
@ -78,25 +76,26 @@ func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany
return domain.ConvertDBCompanyDetails(dbCompany), nil
}
func (s *Store) GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error) {
dbCompanyID, err := s.queries.GetCompanyIDUsingSlug(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))
func (s *Store) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) {
dbCompany, err := s.queries.GetCompanyUsingSlug(ctx, slug)
if err != nil {
return domain.Company{}, err
}
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 {
return s.queries.DeleteCompany(ctx, id)
}

View File

@ -302,6 +302,71 @@ func (s *Store) GetAllEnetpulsePreoddsBettingOffers(ctx context.Context) ([]doma
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 {
// return dbgen.EnetpulseTournamentStage{
// StageID: stage.StageID,
@ -356,14 +421,14 @@ func ConvertDBEnetpulseFixture(dbF dbgen.EnetpulseFixture) domain.EnetpulseFixtu
TournamentTemplateName: dbF.TournamentTemplateName.String,
SportName: dbF.SportName.String,
Gender: dbF.Gender.String,
StartDate: dbF.StartDate.Time,
StartDate: dbF.StartDate.Time.String(),
StatusType: dbF.StatusType.String,
StatusDescFK: dbF.StatusDescFk.String,
RoundTypeFK: dbF.RoundTypeFk.String,
UpdatesCount: int(dbF.UpdatesCount.Int32),
LastUpdatedAt: dbF.LastUpdatedAt.Time,
CreatedAt: dbF.CreatedAt.Time,
UpdatedAt: dbF.UpdatedAt.Time,
UpdatesCount: fmt.Sprintf("%v", dbF.UpdatesCount),
LastUpdatedAt: dbF.LastUpdatedAt.Time.String(),
// CreatedAt: dbF.CreatedAt.Time,
// UpdatedAt: dbF.UpdatedAt.Time,
}
}
@ -672,17 +737,17 @@ func ConvertCreateEnetpulsePreodds(p domain.CreateEnetpulsePreodds) (dbgen.Creat
func ConvertDBEnetpulsePreodds(dbP dbgen.EnetpulsePreodd) domain.EnetpulsePreodds {
return domain.EnetpulsePreodds{
PreoddsID: dbP.PreoddsID,
EventFK: fmt.Sprintf("%v", dbP.EventFk),
OutcomeTypeFK: fmt.Sprintf("%v", dbP.OutcomeTypeFk),
OutcomeScopeFK: fmt.Sprintf("%v", dbP.OutcomeScopeFk),
OutcomeSubtypeFK: fmt.Sprintf("%v", dbP.OutcomeSubtypeFk),
EventParticipantNumber: int(dbP.EventParticipantNumber.Int32),
EventFK: dbP.EventFk,
OutcomeTypeFK: dbP.OutcomeTypeFk.Int32,
OutcomeScopeFK: dbP.OutcomeScopeFk.Int32,
OutcomeSubtypeFK: dbP.OutcomeSubtypeFk.Int32,
EventParticipantNumber: dbP.EventParticipantNumber.Int32,
IParam: dbP.Iparam.String,
IParam2: dbP.Iparam2.String,
DParam: dbP.Dparam.String,
DParam2: dbP.Dparam2.String,
SParam: dbP.Sparam.String,
UpdatesCount: int(dbP.UpdatesCount.Int32),
UpdatesCount: dbP.UpdatesCount.Int32,
LastUpdatedAt: dbP.LastUpdatedAt.Time,
CreatedAt: dbP.CreatedAt.Time,
UpdatedAt: dbP.UpdatedAt.Time,

View File

@ -3,7 +3,6 @@ package repository
import (
"context"
"fmt"
"math"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"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) {
events, err := s.queries.GetAllEvents(ctx, dbgen.GetAllEventsParams{
LeagueID: filter.LeagueID.ToPG(),
SportID: filter.SportID.ToPG(),
Query: filter.Query.ToPG(),
Limit: filter.Limit.ToPG(),
Offset: filter.Offset.ToPG(),
LeagueID: filter.LeagueID.ToPG(),
SportID: filter.SportID.ToPG(),
Query: filter.Query.ToPG(),
Limit: filter.Limit.ToPG(),
Offset: pgtype.Int4{
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
FirstStartTime: filter.FirstStartTime.ToPG(),
LastStartTime: filter.LastStartTime.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
@ -55,18 +58,20 @@ func (s *Store) GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]
return nil, 0, err
}
numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value))
return domain.ConvertDBEvents(events), int64(numberOfPages), nil
return domain.ConvertDBEvents(events), totalCount, nil
}
func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) {
events, err := s.queries.GetEventsWithSettings(ctx, dbgen.GetEventsWithSettingsParams{
CompanyID: companyID,
LeagueID: filter.LeagueID.ToPG(),
SportID: filter.SportID.ToPG(),
Query: filter.Query.ToPG(),
Limit: filter.Limit.ToPG(),
Offset: filter.Offset.ToPG(),
CompanyID: companyID,
LeagueID: filter.LeagueID.ToPG(),
SportID: filter.SportID.ToPG(),
Query: filter.Query.ToPG(),
Limit: filter.Limit.ToPG(),
Offset: pgtype.Int4{
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
FirstStartTime: filter.FirstStartTime.ToPG(),
LastStartTime: filter.LastStartTime.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
@ -99,8 +104,6 @@ func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filt
return nil, 0, err
}
numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value))
result := make([]domain.EventWithSettings, len(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(),
Source: domain.EventSource(event.Source),
Status: domain.EventStatus(event.Status),
TotalOddOutcomes: event.TotalOutcomes,
SourceEventID: event.SourceEventID,
WinningUpperLimit: event.WinningUpperLimit,
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
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) {
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(),
Source: domain.EventSource(event.Source),
Status: domain.EventStatus(event.Status),
TotalOddOutcomes: event.TotalOutcomes,
SourceEventID: event.SourceEventID,
WinningUpperLimit: event.WinningUpperLimit,
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
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 {
return s.queries.SaveEventSettings(ctx, domain.ConvertUpdateEventSettings(event))
func (s *Store) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error {
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 {
err := s.queries.DeleteEvent(ctx, eventID)
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 {
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{
Query: filter.Query.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
@ -31,10 +31,17 @@ func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) (
},
})
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) {
@ -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 {
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 {
CreateNotification(ctx context.Context, notification *domain.Notification) (*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)
ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error)
CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error)
GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error)
GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error)
DeleteOldNotifications(ctx context.Context) error
}
type Repository struct {
@ -69,6 +70,8 @@ func (r *Repository) CreateNotification(ctx context.Context, notification *domai
Payload: marshalPayload(notification.Payload),
Priority: priority,
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,
}
@ -113,7 +116,7 @@ func (r *Repository) GetUserNotifications(ctx context.Context, recipientID int64
if err != nil {
return nil, 0, err
}
var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications))
for _, dbNotif := range dbNotifications {
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))
}
func (s *Repository) DeleteOldNotifications(ctx context.Context) error {
return s.store.queries.DeleteOldNotifications(ctx)
}
func (r *Repository) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification {
var errorSeverity domain.NotificationErrorSeverity
if dbNotif.ErrorSeverity.Valid {
@ -199,6 +206,8 @@ func (r *Repository) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notifica
Payload: payload,
Priority: priority,
Timestamp: dbNotif.Timestamp.Time,
Expires: dbNotif.Expires.Time,
Image: dbNotif.Img.String,
Metadata: dbNotif.Metadata,
}
}

View File

@ -180,16 +180,17 @@ func (s *Store) GetOddsWithSettingsByMarketID(ctx context.Context, marketID int6
}
converted := domain.OddMarketWithSettings{
ID: odds.ID,
EventID: odds.EventID,
MarketType: odds.MarketType,
MarketName: odds.MarketName,
MarketCategory: odds.MarketCategory,
MarketID: odds.MarketID,
RawOdds: rawOdds,
FetchedAt: odds.FetchedAt.Time,
ExpiresAt: odds.ExpiresAt.Time,
IsActive: odds.IsActive,
ID: odds.ID,
EventID: odds.EventID,
MarketType: odds.MarketType,
MarketName: odds.MarketName,
MarketCategory: odds.MarketCategory,
MarketID: odds.MarketID,
NumberOfOutcomes: odds.NumberOfOutcomes,
RawOdds: rawOdds,
FetchedAt: odds.FetchedAt.Time,
ExpiresAt: odds.ExpiresAt.Time,
IsActive: odds.IsActive,
}
return converted, nil
}
@ -221,16 +222,17 @@ func (s *Store) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID
}
converted := domain.OddMarketWithSettings{
ID: odds.ID,
EventID: odds.EventID,
MarketType: odds.MarketType,
MarketName: odds.MarketName,
MarketCategory: odds.MarketCategory,
MarketID: odds.MarketID,
RawOdds: rawOdds,
FetchedAt: odds.FetchedAt.Time,
ExpiresAt: odds.ExpiresAt.Time,
IsActive: odds.IsActive,
ID: odds.ID,
EventID: odds.EventID,
MarketType: odds.MarketType,
MarketName: odds.MarketName,
MarketCategory: odds.MarketCategory,
MarketID: odds.MarketID,
NumberOfOutcomes: odds.NumberOfOutcomes,
RawOdds: rawOdds,
FetchedAt: odds.FetchedAt.Time,
ExpiresAt: odds.ExpiresAt.Time,
IsActive: odds.IsActive,
}
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) {
odds, err := s.queries.GetOddsByEventID(ctx, dbgen.GetOddsByEventIDParams{
EventID: eventID,
Status: filter.Status.ToPG(),
IsLive: filter.IsLive.ToPG(),
Limit: filter.Limit.ToPG(),
Offset: filter.Offset.ToPG(),
IsLive: pgtype.Bool{
Bool: false,
Valid: true,
},
Status: pgtype.Text{
String: string(domain.STATUS_PENDING),
Valid: true,
},
Source: pgtype.Text{},
Source: pgtype.Text{},
})
if err != nil {
return nil, err
@ -293,16 +289,17 @@ func (s *Store) GetOddsWithSettingsByEventID(ctx context.Context, eventID int64,
}
result[i] = domain.OddMarketWithSettings{
ID: o.ID,
EventID: o.EventID,
MarketType: o.MarketType,
MarketName: o.MarketName,
MarketCategory: o.MarketCategory,
MarketID: o.MarketID,
RawOdds: rawOdds,
FetchedAt: o.FetchedAt.Time,
ExpiresAt: o.ExpiresAt.Time,
IsActive: o.IsActive,
ID: o.ID,
EventID: o.EventID,
MarketType: o.MarketType,
MarketName: o.MarketName,
MarketCategory: o.MarketCategory,
MarketID: o.MarketID,
NumberOfOutcomes: o.NumberOfOutcomes,
RawOdds: rawOdds,
FetchedAt: o.FetchedAt.Time,
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)
}
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 {
return domain.Raffle{
ID: raffle.ID,
CompanyID: raffle.CompanyID,
Name: raffle.Name,
CreatedAt: raffle.CreatedAt.Time,
ExpiresAt: raffle.ExpiresAt.Time,
Type: raffle.Type,
Status: raffle.Status,
ID: raffle.ID,
CompanyID: raffle.CompanyID,
Name: raffle.Name,
CreatedAt: raffle.CreatedAt.Time,
ExpiresAt: raffle.ExpiresAt.Time,
TicketLimit: raffle.TicketLimit,
Type: raffle.Type,
Status: raffle.Status,
}
}
@ -48,7 +49,8 @@ func convertCreateRaffle(raffle domain.CreateRaffle) dbgen.CreateRaffleParams {
Time: *raffle.ExpiresAt,
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
}
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,
FullName: bet.CustomerFullName,
PhoneNumber: bet.CustomerPhoneNumber,
FastCode: bet.FastCode,
CashoutID: bet.CashoutID,
CashedOut: bet.CashedOut,
BetID: bet.BetID,
@ -63,7 +64,7 @@ func (s *Store) CreateShopBet(ctx context.Context, bet domain.CreateShopBet) (do
if err != nil {
return domain.ShopBet{}, 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())
return domain.ShopBetDetail{}, err
}
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,
},
Offset: pgtype.Int4{
Int32: int32(filter.Page.Value),
Int32: int32(filter.Page.Value * filter.PageSize.Value),
Valid: filter.Page.Valid,
},
Query: pgtype.Text{

View File

@ -4,7 +4,6 @@ import (
"context"
"database/sql"
"errors"
"fmt"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -30,7 +29,7 @@ type VirtualGameRepository interface {
RemoveFavoriteGame(ctx context.Context, userID, gameID 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)
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) {
query := `SELECT
COUNT(*) as total,
COUNT(CASE WHEN is_active = true THEN 1 END) as active,
COUNT(CASE WHEN is_active = false THEN 1 END) as inactive
FROM virtual_games`
// func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) {
// query := `SELECT
// COUNT(*) as total,
// COUNT(CASE WHEN is_active = true THEN 1 END) as active,
// COUNT(CASE WHEN is_active = false THEN 1 END) as inactive
// FROM virtual_games`
args := []interface{}{}
argPos := 1
// args := []interface{}{}
// argPos := 1
// Add filters if provided
if filter.StartTime.Valid {
query += fmt.Sprintf(" WHERE created_at >= $%d", argPos)
args = append(args, filter.StartTime.Value)
argPos++
}
if filter.EndTime.Valid {
query += fmt.Sprintf(" AND created_at <= $%d", argPos)
args = append(args, filter.EndTime.Value)
argPos++
}
// // Add filters if provided
// if filter.StartTime.Valid {
// query += fmt.Sprintf(" WHERE created_at >= $%d", argPos)
// args = append(args, filter.StartTime.Value)
// argPos++
// }
// if filter.EndTime.Valid {
// query += fmt.Sprintf(" AND created_at <= $%d", argPos)
// args = append(args, filter.EndTime.Value)
// argPos++
// }
row := r.store.conn.QueryRow(ctx, query, args...)
err = row.Scan(&total, &active, &inactive)
if err != nil {
return 0, 0, 0, fmt.Errorf("failed to get game counts: %w", err)
}
// row := r.store.conn.QueryRow(ctx, query, args...)
// err = row.Scan(&total, &active, &inactive)
// if err != nil {
// 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) {
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 {
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) {
newWallet, err := s.queries.CreateWallet(ctx, convertCreateWallet(wallet))
if err != nil {
if IsUniqueViolation(err) {
return domain.Wallet{}, domain.ErrWalletIDDuplicate
}
return domain.Wallet{}, err
}
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 _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail,
// domain.DeliveryChannelEmail,
} {
n := newBetResultNotification(user.ID, domain.NotificationLevelError, channel, headline, message, map[string]any{
"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 {
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)
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 _, channel := range []domain.DeliveryChannel{
domain.DeliveryChannelInApp,
domain.DeliveryChannelEmail,
// domain.DeliveryChannelEmail,
} {
raw, _ := json.Marshal(map[string]any{
"winnings": totalWinnings,

View File

@ -15,6 +15,7 @@ type BetStore interface {
GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, int64, error)
GetBetByUserID(ctx context.Context, UserID int64) ([]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)
GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, 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)
UpdateBetOutcomeStatusByBetID(ctx context.Context, id 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) (
totalStakes domain.Currency,
totalBets int64,

View File

@ -35,8 +35,10 @@ var (
ErrGenerateRandomOutcome = errors.New("failed to generate any random outcome for events")
ErrOutcomesNotCompleted = errors.New("some bet outcomes are still pending")
ErrEventHasBeenRemoved = errors.New("event has been removed")
ErrEventHasBeenDisabled = errors.New("event has been disabled")
ErrEventHasNotEnded = errors.New("event has not ended yet")
ErrOddHasBeenDisabled = errors.New("odd has been disabled")
ErrRawOddInvalid = errors.New("prematch Raw Odd is Invalid")
ErrBranchIDRequired = errors.New("branch ID required for this role")
ErrOutcomeLimit = errors.New("too many outcomes on a single bet")
@ -46,6 +48,8 @@ var (
ErrInvalidAmount = errors.New("invalid amount")
ErrBetAmountTooHigh = errors.New("cannot create a bet with an amount above limit")
ErrBetWinningTooHigh = errors.New("total Winnings over set limit")
ErrCompanyDeductedPercentInvalid = errors.New("invalid company deducted percentage")
)
type Service struct {
@ -106,10 +110,10 @@ func (s *Service) GenerateCashoutID() (string, error) {
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)
event, err := s.eventSvc.GetEventByID(ctx, eventID)
event, err := s.eventSvc.GetEventWithSettingByID(ctx, eventID, companyID)
if err != nil {
s.mongoLogger.Error("failed to fetch upcoming event by ID",
zap.Int64("event_id", eventID),
@ -118,6 +122,14 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
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()
if event.StartTime.Before(currentTime) {
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
}
odds, err := s.prematchSvc.GetOddsByMarketID(ctx, marketID, eventID)
odds, err := s.prematchSvc.GetOddsWithSettingsByMarketID(ctx, marketID, eventID, companyID)
if err != nil {
s.mongoLogger.Error("failed to get raw odds by market ID",
zap.Int64("event_id", eventID),
@ -138,6 +150,15 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
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 {
ID string
Name string
@ -255,7 +276,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
var totalOdds float32 = 1
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 {
s.mongoLogger.Error("failed to generate outcome",
zap.Int64("event_id", outcomeReq.EventID),
@ -303,8 +324,9 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
}
fastCode := helpers.GenerateFastCode()
accumulator := calculateAccumulator(len(outcomes))
amount := req.Amount + (req.Amount * accumulator)
// accumulator := calculateAccumulator(len(outcomes))
// amount := req.Amount + (req.Amount * accumulator)
amount := req.Amount
newBet := domain.CreateBet{
Amount: domain.ToCurrency(amount),
@ -524,7 +546,27 @@ func (s *Service) DeductBetFromBranchWallet(ctx context.Context, amount float32,
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
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,
walletID, domain.ToCurrency(deductedAmount), domain.ValidInt64{
Value: userID,
@ -848,6 +890,9 @@ func (s *Service) GetBetOutcomeByBetID(ctx context.Context, UserID int64) ([]dom
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) {
return s.betStore.GetBetOutcomeByEventID(ctx, eventID, is_filtered)
}
@ -1076,6 +1121,32 @@ func (s *Service) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID in
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 {
_, err := s.betStore.UpdateBetOutcomeStatusByBetID(ctx, id, domain.OUTCOME_STATUS_VOID)
if err != nil {

View File

@ -12,7 +12,7 @@ type BranchStore interface {
GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error)
GetBranchByCompanyID(ctx context.Context, companyID int64) ([]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)
DeleteBranch(ctx context.Context, id int64) 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)
}
func (s *Service) SearchBranchByName(ctx context.Context, name string) ([]domain.BranchDetail, error) {
return s.branchStore.SearchBranchByName(ctx, name)
func (s *Service) SearchBranchByName(ctx context.Context, name string, companyID domain.ValidInt64) ([]domain.BranchDetail, error) {
return s.branchStore.SearchBranchByName(ctx, name, companyID)
}
func (s *Service) UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error) {
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)
SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error)
GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error)
GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error)
UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error)
GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error)
UpdateCompany(ctx context.Context, company domain.UpdateCompany) (error)
DeleteCompany(ctx context.Context, id int64) 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) {
return s.companyStore.GetCompanyByID(ctx, id)
}
func (s *Service) GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error){
return s.companyStore.GetCompanyIDBySlug(ctx, slug)
func (s *Service) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) {
return s.companyStore.GetCompanyBySlug(ctx, slug)
}
func (s *Service) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) {
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)
}
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
}
@ -367,7 +367,7 @@ func (s *Service) FetchAndStoreTournamentStages(ctx context.Context) error {
for _, t := range tournaments {
// Compose URL for each tournament
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,
s.cfg.EnetPulseConfig.UserName,
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 {
// 1⃣ Fetch all sports from the database
// 1⃣ Fetch all sports from DB
sports, err := s.store.GetAllEnetpulseSports(ctx)
if err != nil {
return 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"`
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
// Define API fixture struct
type Fixture struct {
FixtureID string `json:"id"`
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"`
SportName string `json:"sport_name"`
Gender string `json:"gender"`
StartDate string `json:"startdate"`
StartDate string `json:"startdate"` // ISO 8601
StatusType string `json:"status_type"`
StatusDescFK string `json:"status_descFK"`
RoundTypeFK string `json:"round_typeFK"`
UpdatesCount string `json:"n"`
LastUpdatedAt string `json:"ut"`
UpdatesCount string `json:"n"` // convert to int
LastUpdatedAt string `json:"ut"` // parse to time.Time
}
// 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",
"https://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,
@ -616,7 +503,7 @@ func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.Enet
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)
fmt.Printf("creating request for sport %s: %v\n", sport.SportID, err)
continue
}
@ -639,29 +526,38 @@ func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.Enet
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)
fmt.Printf("decoding fixtures for sport %s: %v\n", sport.SportID, err)
continue
}
// 4⃣ Iterate over fixtures and store them
// 4⃣ Iterate and upsert fixtures
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)
// Parse StartDate and LastUpdatedAt
startDate, err := time.Parse(time.RFC3339, fx.StartDate)
if err != nil {
fmt.Printf("invalid startDate for fixture %s: %v\n", fx.FixtureID, err)
continue
}
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)
lastUpdatedAt, _ := time.Parse(time.RFC3339, fx.LastUpdatedAt)
// Convert UpdatesCount
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,
Name: fx.Name,
SportFK: fx.SportFK,
TournamentFK: strconv.Itoa(tournamentFK),
TournamentTemplateFK: strconv.Itoa(tournamentTemplateFK),
TournamentStageFK: strconv.Itoa(tournamentStageFK),
TournamentFK: fx.TournamentFK,
TournamentTemplateFK: fx.TournamentTemplateFK,
TournamentStageFK: fx.TournamentStageFK,
TournamentStageName: fx.TournamentStageName,
TournamentName: fx.TournamentName,
TournamentTemplateName: fx.TournamentTemplateName,
@ -669,29 +565,150 @@ func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.Enet
Gender: fx.Gender,
StartDate: startDate,
StatusType: fx.StatusType,
StatusDescFK: strconv.Itoa(statusDescFK),
RoundTypeFK: strconv.Itoa(roundTypeFK),
StatusDescFK: fx.StatusDescFK,
RoundTypeFK: fx.RoundTypeFK,
UpdatesCount: updatesCount,
LastUpdatedAt: lastUpdatedAt,
LastUpdatedAt: lastUpdated,
}
dbFixture, err := s.store.CreateEnetpulseFixture(ctx, createFixture)
if err != nil {
fmt.Printf("failed storing fixture %s: %v\n", fx.FixtureID, err)
// 5⃣ Save fixture using UPSERT repository method
if _, err := s.store.CreateEnetpulseFixture(ctx, fixture); err != nil {
fmt.Printf("failed upserting 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
break
}
fmt.Println("✅ Completed fetching and storing fixtures for all sports")
return allFixtures, nil
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 {
// 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) {
// 1⃣ Fetch all from store
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 {
// 1⃣ Fetch all events from DB
// 1⃣ Fetch all fixtures
fixtures, err := s.store.GetAllEnetpulseFixtures(ctx)
if err != nil {
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 {
url := fmt.Sprintf(
"http://eapi.enetpulse.com/preodds/event/?objectFK=%s&odds_providerFK=%s&username=%s&token=%s",
fixture.FixtureID,
s.cfg.EnetPulseConfig.ProviderID,
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
// 4⃣ Loop through each outcome type
for _, outcome := range outcomeTypes {
url := fmt.Sprintf(
"http://eapi.enetpulse.com/preodds/event/?objectFK=%s&odds_providerFK=%s&outcome_typeFK=%s&username=%s&token=%s",
fixture.FixtureID,
s.cfg.EnetPulseConfig.ProviderID,
outcome.OutcomeTypeID,
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
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)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
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
resp, err := s.httpClient.Do(req)
if err != nil {
continue
}
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{
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,
eventParticipantNumber := int32(0)
if p.EventParticipantNumber != "" {
if epn, err := strconv.Atoi(p.EventParticipantNumber); err == nil {
eventParticipantNumber = int32(epn)
}
}
_, _ = 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
}
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
// func parseStringToInt32(s string) int32 {
// if s == "" {

View File

@ -18,6 +18,7 @@ type Service interface {
UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) 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)
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)
}

View File

@ -491,8 +491,11 @@ func (s *service) GetEventWithSettingByID(ctx context.Context, ID int64, company
return s.store.GetEventWithSettingByID(ctx, ID, companyID)
}
func (s *service) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error {
return s.store.UpdateEventSettings(ctx, event)
func (s *service) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error {
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) {

View File

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

View File

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

View File

@ -9,8 +9,9 @@ import (
type Service interface {
SaveLeague(ctx context.Context, league domain.CreateLeague) error
SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error
GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error)
GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, 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)
CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, 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)
}
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)
}
@ -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 {
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 (
"context"
"encoding/json"
"fmt"
// "errors"
@ -12,19 +11,19 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
"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/repository"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/segmentio/kafka-go"
// "github.com/segmentio/kafka-go"
"go.uber.org/zap"
// "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws"
// afro "github.com/amanuelabay/afrosms-go"
"github.com/gorilla/websocket"
"github.com/redis/go-redis/v9"
// "github.com/redis/go-redis/v9"
)
type Service struct {
@ -39,8 +38,6 @@ type Service struct {
messengerSvc *messenger.Service
mongoLogger *zap.Logger
logger *slog.Logger
redisClient *redis.Client
reader *kafka.Reader
}
func New(repo repository.NotificationRepository,
@ -49,17 +46,8 @@ func New(repo repository.NotificationRepository,
cfg *config.Config,
messengerSvc *messenger.Service,
userSvc *user.Service,
kafkaBrokers []string,
) *Service {
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{
repo: repo,
@ -72,15 +60,13 @@ func New(repo repository.NotificationRepository,
messengerSvc: messengerSvc,
userSvc: userSvc,
config: cfg,
redisClient: rdb,
reader: walletReader,
}
go hub.Run()
go svc.startWorker()
go svc.startRetryWorker()
go svc.RunRedisSubscriber(context.Background())
go svc.StartKafkaConsumer(context.Background())
// go svc.RunRedisSubscriber(context.Background())
// go svc.StartKafkaConsumer(context.Background())
return svc
}
@ -484,189 +470,192 @@ func (s *Service) CountUnreadNotifications(ctx context.Context, recipient_id int
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){
// return s.repo.Get(ctx, filter)
// }
func (s *Service) RunRedisSubscriber(ctx context.Context) {
pubsub := s.redisClient.Subscribe(ctx, "live_metrics")
defer pubsub.Close()
// func (s *Service) RunRedisSubscriber(ctx context.Context) {
// pubsub := s.redisClient.Subscribe(ctx, "live_metrics")
// defer pubsub.Close()
ch := pubsub.Channel()
for msg := range ch {
var parsed map[string]interface{}
if err := json.Unmarshal([]byte(msg.Payload), &parsed); err != nil {
// s.logger.Error("invalid Redis message format", "payload", msg.Payload, "error", err)
s.mongoLogger.Error("invalid Redis message format",
zap.String("payload", msg.Payload),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
continue
}
// ch := pubsub.Channel()
// for msg := range ch {
// var parsed map[string]interface{}
// if err := json.Unmarshal([]byte(msg.Payload), &parsed); err != nil {
// // s.logger.Error("invalid Redis message format", "payload", msg.Payload, "error", err)
// s.mongoLogger.Error("invalid Redis message format",
// zap.String("payload", msg.Payload),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// continue
// }
eventType, _ := parsed["type"].(string)
payload := parsed["payload"]
recipientID, hasRecipient := parsed["recipient_id"]
recipientType, _ := parsed["recipient_type"].(string)
// eventType, _ := parsed["type"].(string)
// payload := parsed["payload"]
// recipientID, hasRecipient := parsed["recipient_id"]
// recipientType, _ := parsed["recipient_type"].(string)
message := map[string]interface{}{
"type": eventType,
"payload": payload,
}
// message := map[string]interface{}{
// "type": eventType,
// "payload": payload,
// }
if hasRecipient {
message["recipient_id"] = recipientID
message["recipient_type"] = recipientType
}
// if hasRecipient {
// message["recipient_id"] = recipientID
// 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 {
const key = "live_metrics"
// func (s *Service) UpdateLiveWalletMetrics(ctx context.Context, companies []domain.GetCompany, branches []domain.BranchWallet) error {
// const key = "live_metrics"
companyBalances := make([]domain.CompanyWalletBalance, 0, len(companies))
for _, c := range companies {
companyBalances = append(companyBalances, domain.CompanyWalletBalance{
CompanyID: c.ID,
CompanyName: c.Name,
Balance: float64(c.WalletBalance.Float32()),
})
}
// companyBalances := make([]domain.CompanyWalletBalance, 0, len(companies))
// for _, c := range companies {
// companyBalances = append(companyBalances, domain.CompanyWalletBalance{
// CompanyID: c.ID,
// CompanyName: c.Name,
// Balance: float64(c.WalletBalance.Float32()),
// })
// }
branchBalances := make([]domain.BranchWalletBalance, 0, len(branches))
for _, b := range branches {
branchBalances = append(branchBalances, domain.BranchWalletBalance{
BranchID: b.ID,
BranchName: b.Name,
CompanyID: b.CompanyID,
Balance: float64(b.Balance.Float32()),
})
}
// branchBalances := make([]domain.BranchWalletBalance, 0, len(branches))
// for _, b := range branches {
// branchBalances = append(branchBalances, domain.BranchWalletBalance{
// BranchID: b.ID,
// BranchName: b.Name,
// CompanyID: b.CompanyID,
// Balance: float64(b.Balance.Float32()),
// })
// }
payload := domain.LiveWalletMetrics{
Timestamp: time.Now(),
CompanyBalances: companyBalances,
BranchBalances: branchBalances,
}
// payload := domain.LiveWalletMetrics{
// Timestamp: time.Now(),
// CompanyBalances: companyBalances,
// BranchBalances: branchBalances,
// }
updatedData, err := json.Marshal(payload)
if err != nil {
return err
}
// updatedData, err := json.Marshal(payload)
// if err != nil {
// return err
// }
if err := s.redisClient.Set(ctx, key, updatedData, 0).Err(); err != nil {
return err
}
// if err := s.redisClient.Set(ctx, key, updatedData, 0).Err(); err != nil {
// return err
// }
if err := s.redisClient.Publish(ctx, key, updatedData).Err(); err != nil {
return err
}
return nil
}
// if err := s.redisClient.Publish(ctx, key, updatedData).Err(); err != nil {
// return err
// }
// return nil
// }
func (s *Service) GetLiveMetrics(ctx context.Context) (domain.LiveMetric, error) {
const key = "live_metrics"
var metric domain.LiveMetric
// func (s *Service) GetLiveMetrics(ctx context.Context) (domain.LiveMetric, error) {
// const key = "live_metrics"
// var metric domain.LiveMetric
val, err := s.redisClient.Get(ctx, key).Result()
if err == redis.Nil {
// Key does not exist yet, return zero-valued struct
return domain.LiveMetric{}, nil
} else if err != nil {
return domain.LiveMetric{}, err
}
// val, err := s.redisClient.Get(ctx, key).Result()
// if err == redis.Nil {
// // Key does not exist yet, return zero-valued struct
// return domain.LiveMetric{}, nil
// } else if err != nil {
// return domain.LiveMetric{}, err
// }
if err := json.Unmarshal([]byte(val), &metric); err != nil {
return domain.LiveMetric{}, err
}
// if err := json.Unmarshal([]byte(val), &metric); err != nil {
// 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) {
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
}
// var walletEvent event.WalletEvent
// if err := json.Unmarshal(m.Value, &walletEvent); err != nil {
// s.mongoLogger.Error("[NotificationSvc.KafkaConsumer] Failed to unmarshal wallet event",
// zap.String("message", string(m.Value)),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// continue
// }
var walletEvent event.WalletEvent
if err := json.Unmarshal(m.Value, &walletEvent); err != nil {
s.mongoLogger.Error("[NotificationSvc.KafkaConsumer] Failed to unmarshal wallet event",
zap.String("message", string(m.Value)),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
continue
}
// raw, _ := json.Marshal(map[string]any{
// "balance": walletEvent.Balance.Float32(),
// "type": walletEvent.WalletType,
// "timestamp": time.Now(),
// })
raw, _ := json.Marshal(map[string]any{
"balance": walletEvent.Balance.Float32(),
"type": walletEvent.WalletType,
"timestamp": time.Now(),
})
// headline := ""
// message := ""
// var receiver domain.NotificationRecieverSide
// switch walletEvent.WalletType {
headline := ""
message := ""
var receiver domain.NotificationRecieverSide
switch walletEvent.WalletType {
// case domain.StaticWalletType:
// headline = "Referral and Bonus Wallet Updated"
// message = fmt.Sprintf("Your referral and bonus wallet balance is now %.2f", walletEvent.Balance.Float32())
// 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:
headline = "Referral and Bonus Wallet Updated"
message = fmt.Sprintf("Your referral and bonus wallet balance is now %.2f", walletEvent.Balance.Float32())
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,
},
}
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()),
)
}
}
}()
}
// 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) {
// var (

View File

@ -17,31 +17,17 @@ type Service interface {
GetALLPrematchOdds(ctx context.Context) ([]domain.OddMarket, error)
// GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.OddMarket, error)
DeleteOddsForEvent(ctx context.Context, eventID string) error
GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error)
GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error)
GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error)
// Settings
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
InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error)
GetAllOddHistory(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{
EventID: eventID,
MarketCategory: sectionName,
MarketType: marketType,
MarketName: market.Name,
MarketID: marketIDint,
UpdatedAt: updatedAt,
Odds: marketOdds,
EventID: eventID,
MarketCategory: sectionName,
MarketType: marketType,
MarketName: market.Name,
MarketID: marketIDint,
NumberOfOutcomes: int64(len(market.Odds)),
UpdatedAt: updatedAt,
Odds: marketOdds,
// 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)
}
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 {
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)
}
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 {
// if str, ok := v.(string); ok {
// return str

View File

@ -16,9 +16,12 @@ type RaffleStore interface {
CreateRaffleWinner(ctx context.Context, raffleWinnerParams domain.RaffleWinnerParams) error
SetRaffleComplete(ctx context.Context, raffleID int32) 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)
GetUserRaffleTickets(ctx context.Context, userID int32) ([]domain.RaffleTicketRes, error)
SuspendRaffleTicket(ctx context.Context, raffleTicketID 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) {
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
summary.TotalGames, summary.ActiveGames, summary.InactiveGames, err = s.virtulaGamesStore.GetGameCounts(ctx, filter)
if err != nil {
s.logger.Error("failed to get game counts", "error", err)
return domain.DashboardSummary{}, err
}
// summary.TotalGames, summary.ActiveGames, summary.InactiveGames, err = s.virtulaGamesStore.GetGameCounts(ctx, filter)
// if err != nil {
// s.logger.Error("failed to get game counts", "error", err)
// return domain.DashboardSummary{}, err
// }
// Get company metrics
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(
zap.Int64("eventID", event.ID),
)
result, err := s.FetchB365Result(ctx, event.SourceEventID)
if err != nil {
if err == ErrEventIsNotActive {
@ -457,276 +456,6 @@ func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error {
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) {
events, _, err := s.repo.GetAllEvents(ctx, domain.EventFilter{
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{
Amount: domain.Currency(req.Amount),
Amount: domain.ToCurrency(req.Amount),
BranchID: branchID,
CompanyID: companyID,
UserID: userID,

View File

@ -23,7 +23,7 @@ type VirtualGameService interface {
ProcessTournamentWin(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)
RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, 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
}
func (s *service) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) {
return s.repo.GetGameCounts(ctx, filter)
}
// func (s *service) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) {
// return s.repo.GetGameCounts(ctx, filter)
// }
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

View File

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

View File

@ -2,10 +2,12 @@ package wallet
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"go.uber.org/zap"
"time"
)
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
}
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 {
// Send different messages
// Send notification to admin team
adminNotification := &domain.Notification{
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 {
s.mongoLogger.Error("failed to send email admin notification",
zap.Int64("admin_id", adminID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
// if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil {
// s.mongoLogger.Error("failed to send email admin notification",
// zap.Int64("admin_id", adminID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return err
// }
}
return nil
}

View File

@ -3,7 +3,7 @@ package wallet
import (
"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"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"go.uber.org/zap"
@ -18,7 +18,6 @@ type Service struct {
userSvc *user.Service
mongoLogger *zap.Logger
logger *slog.Logger
kafkaProducer *kafka.Producer
}
func NewService(
@ -29,7 +28,6 @@ func NewService(
userSvc *user.Service,
mongoLogger *zap.Logger,
logger *slog.Logger,
kafkaProducer *kafka.Producer,
) *Service {
return &Service{
walletStore: walletStore,
@ -40,6 +38,5 @@ func NewService(
userSvc: userSvc,
mongoLogger: mongoLogger,
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)
if err != nil {
return domain.Transfer{}, err
}
if !senderWallet.IsActive {
return domain.Transfer{}, ErrWalletIsDisabled
}
if !senderWallet.IsTransferable {
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 {
return domain.Transfer{}, err
}
if !receiverWallet.IsActive {
return domain.Transfer{}, ErrWalletIsDisabled
}
if !receiverWallet.IsTransferable {
fmt.Printf("Error: %d Receiver Wallet is not transferable \n", senderWallet.ID)
return domain.Transfer{}, ErrReceiverWalletNotTransferable

View File

@ -3,14 +3,17 @@ package wallet
import (
"context"
"errors"
"fmt"
// "fmt"
"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 (
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) {
@ -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 {
err := s.walletStore.UpdateBalance(ctx, id, balance)
if err != nil {
return err
}
wallet, err := s.GetWalletByID(ctx, id)
if err != nil {
return err
}
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 !wallet.IsActive {
return ErrWalletIsDisabled
}
err = s.walletStore.UpdateBalance(ctx, id, balance)
if err != nil {
return err
}
// 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
}
@ -112,22 +126,29 @@ func (s *Service) AddToWallet(
if err != nil {
return domain.Transfer{}, err
}
if !wallet.IsActive {
return domain.Transfer{}, ErrWalletIsDisabled
}
err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount)
if err != nil {
return domain.Transfer{}, err
}
go func() {
s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{
EventType: event.WalletBalanceUpdated,
WalletID: wallet.ID,
UserID: wallet.UserID,
Balance: wallet.Balance + amount,
WalletType: wallet.Type,
Trigger: "AddToWallet",
})
}()
// go func() {
// s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{
// EventType: event.WalletBalanceUpdated,
// WalletID: wallet.ID,
// UserID: wallet.UserID,
// Balance: wallet.Balance + amount,
// WalletType: wallet.Type,
// 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
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
}
if !wallet.IsActive {
return domain.Transfer{}, ErrWalletIsDisabled
}
if wallet.Balance < amount {
// Send Wallet low to admin
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()
if balance < thresholds[0] {
s.SendAdminWalletLowNotification(ctx, wallet)
for _, thresholds := range thresholds {
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
}
go func() {
s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{
EventType: event.WalletBalanceUpdated,
WalletID: wallet.ID,
UserID: wallet.UserID,
Balance: wallet.Balance - amount,
WalletType: wallet.Type,
Trigger: "DeductFromWallet",
})
}()
// go func() {
// s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{
// EventType: event.WalletBalanceUpdated,
// WalletID: wallet.ID,
// UserID: wallet.UserID,
// Balance: wallet.Balance - amount,
// WalletType: wallet.Type,
// 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
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{

View File

@ -11,6 +11,7 @@ import (
betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse"
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"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/report"
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")
}
func StartTicketCrons(ticketService ticket.Service, mongoLogger *zap.Logger) {
func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) {
c := cron.New(cron.WithSeconds())
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 {
@ -261,7 +275,7 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreSports(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store sports", zap.Error(err))
} else {
mongoLogger.Info("✅ Completed fetching and storing sports")
mongoLogger.Info("\n\n✅ Completed fetching and storing sports\n\n")
}
// 2⃣ Tournament Templates
@ -269,7 +283,7 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreTournamentTemplates(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store tournament templates", zap.Error(err))
} else {
mongoLogger.Info("✅ Completed fetching and storing tournament templates")
mongoLogger.Info("\n\n✅ Completed fetching and storing tournament templates\n\n")
}
// 3⃣ Tournaments
@ -277,7 +291,7 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreTournaments(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store tournaments", zap.Error(err))
} else {
mongoLogger.Info("✅ Completed fetching and storing tournaments")
mongoLogger.Info("\n\n✅ Completed fetching and storing tournaments\n\n")
}
// 4⃣ Tournament Stages
@ -285,16 +299,16 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreTournamentStages(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store tournament stages", zap.Error(err))
} 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")
today := time.Now().Format("2006-01-02")
if err := enetPulseSvc.FetchAndStoreFixtures(ctx, today); err != nil {
mongoLogger.Error("Failed to fetch and store fixtures", zap.Error(err))
} else {
mongoLogger.Info("✅ Completed fetching and storing fixtures")
mongoLogger.Info("\n\n✅ Completed fetching and storing fixtures\n\n")
}
// 6⃣ Results
@ -302,7 +316,7 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreResults(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store results", zap.Error(err))
} else {
mongoLogger.Info("✅ Completed fetching and storing results")
mongoLogger.Info("\n\n✅ Completed fetching and storing results\n\n")
}
// 7 Outcome Types
@ -310,15 +324,15 @@ func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger
if err := enetPulseSvc.FetchAndStoreOutcomeTypes(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store outcome_types", zap.Error(err))
} 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")
if err := enetPulseSvc.FetchAndStorePreodds(ctx); err != nil {
mongoLogger.Error("Failed to fetch and store preodds", zap.Error(err))
} 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())
}
if req.CompanyID != nil {
_, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
ID: *req.CompanyID,
AdminID: domain.ValidInt64{
Value: newUser.ID,
Valid: true,
},
})
if err != nil {
h.mongoLoggerSvc.Error("failed to update company with new admin",
zap.Int64("status_code", fiber.StatusInternalServerError),
zap.Int64("company_id", *req.CompanyID),
zap.Int64("admin_id", newUser.ID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company"+err.Error())
}
}
// if req.CompanyID != nil {
// _, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
// ID: *req.CompanyID,
// AdminID: domain.ValidInt64{
// Value: newUser.ID,
// Valid: true,
// },
// })
// if err != nil {
// h.mongoLoggerSvc.Error("failed to update company with new admin",
// zap.Int64("status_code", fiber.StatusInternalServerError),
// zap.Int64("company_id", *req.CompanyID),
// zap.Int64("admin_id", newUser.ID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company"+err.Error())
// }
// }
h.mongoLoggerSvc.Info("admin created successfully",
zap.Int64("admin_id", newUser.ID),
@ -196,7 +196,7 @@ func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
Valid: true,
}
}
companyFilter := int64(c.QueryInt("company_id"))
filter := domain.UserFilter{
Role: string(domain.RoleAdmin),
@ -283,7 +283,7 @@ func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
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
@ -451,24 +451,24 @@ func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update admin:"+err.Error())
}
if req.CompanyID != nil {
_, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
ID: *req.CompanyID,
AdminID: domain.ValidInt64{
Value: AdminID,
Valid: true,
},
})
if err != nil {
h.mongoLoggerSvc.Error("UpdateAdmin failed to update company",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Int64("admin_id", AdminID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company:"+err.Error())
}
}
// if req.CompanyID != nil {
// _, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
// ID: *req.CompanyID,
// AdminID: domain.ValidInt64{
// Value: AdminID,
// Valid: true,
// },
// })
// if err != nil {
// h.mongoLoggerSvc.Error("UpdateAdmin failed to update company",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Int64("admin_id", AdminID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company:"+err.Error())
// }
// }
h.mongoLoggerSvc.Info("UpdateAdmin succeeded",
zap.Int("status_code", fiber.StatusOK),

View File

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

View File

@ -259,24 +259,53 @@ func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userI
sportAndLeagueIDs = append(sportAndLeagueIDs, ids)
}
fmt.Println("sportAndLeagueIDs: ", sportAndLeagueIDs)
for _, raffle := range raffles {
// TODO: only fetch pending raffles from db
if raffle.Status == "completed" {
continue
}
// only require one sport and league combo to be valide to make the raffle ticket
foundValid := false
raffleTicketLimit, err := h.raffleSvc.GetRaffleTicketLimit(c.Context(), raffle.ID)
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 {
if foundValid {
break
}
res, err := h.raffleSvc.CheckValidSportRaffleFilter(c.Context(), raffle.ID, sportAndLeagueID[0], sportAndLeagueID[1])
if err != nil {
continue
}
fmt.Println(sportAndLeagueID, res)
foundValid = foundValid || res
}
@ -289,7 +318,7 @@ func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userI
UserID: int32(userID),
}
_, err := h.raffleSvc.CreateRaffleTicket(c.Context(), raffleTicket)
_, err = h.raffleSvc.CreateRaffleTicket(c.Context(), raffleTicket)
if err != nil {
h.mongoLoggerSvc.Error("Failed to create raffle ticket",
zap.Int("status_code", fiber.StatusInternalServerError),
@ -552,6 +581,25 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
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{
IsShopBet: isShopBet,
Query: searchString,
@ -560,6 +608,7 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
Status: statusFilter,
Limit: limit,
Offset: offset,
CompanyID: companyID,
})
if err != nil {
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))
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
role := c.Locals("role").(domain.Role)
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10)
@ -608,7 +656,7 @@ func (h *Handler) GetAllTenantBets(c *fiber.Ctx) error {
}
var isShopBet domain.ValidBool
isShopBetQuery := c.Query("is_shop")
if isShopBetQuery != "" && role == domain.RoleSuperAdmin {
if isShopBetQuery != "" {
isShopBetParse, err := strconv.ParseBool(isShopBetQuery)
if err != nil {
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
// @Router /api/v1/sport/bet/{id} [get]
func (h *Handler) GetBetByID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
betID := c.Params("id")
id, err := strconv.ParseInt(betID, 10, 64)
if err != nil {
@ -751,6 +801,15 @@ func (h *Handler) GetBetByID(c *fiber.Ctx) error {
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",
// zap.Int64("betID", id),
// 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
// @Router /api/v1/search/branch [get]
func (h *Handler) SearchBranch(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
// Get search query from request
searchQuery := c.Query("q")
if searchQuery == "" {
@ -622,7 +624,7 @@ func (h *Handler) SearchBranch(c *fiber.Ctx) error {
}
// 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 {
h.mongoLoggerSvc.Info("Failed to search branches",
zap.String("query", searchQuery),

View File

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

View File

@ -1,6 +1,7 @@
package handlers
import (
"errors"
"fmt"
"strconv"
"time"
@ -82,17 +83,31 @@ func (h *Handler) CreateCompany(c *fiber.Ctx) error {
AdminID: user.ID,
WalletID: newWallet.ID,
DeductedPercentage: req.DeductedPercentage,
Slug: req.Slug,
IsActive: req.IsActive,
})
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",
zap.Int64("userID", user.ID),
zap.String("name", req.Name),
zap.String("Name", req.Name),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
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)
}
company, err := h.companySvc.UpdateCompany(c.Context(), domain.ConvertUpdateCompanyReq(req))
err = h.companySvc.UpdateCompany(c.Context(), domain.ConvertUpdateCompanyReq(req, id))
if err != nil {
h.mongoLoggerSvc.Error("Failed to update company",
zap.Int64("companyID", id),
zap.Any("req", req),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
@ -373,9 +389,7 @@ func (h *Handler) UpdateCompany(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
res := domain.ConvertCompany(company)
return response.WriteJSON(c, fiber.StatusOK, "Company Updated", res, nil)
return response.WriteJSON(c, fiber.StatusOK, "Company Updated", nil, 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
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(
c.Context(), domain.EventFilter{
SportID: sportID,
@ -146,6 +180,8 @@ func (h *Handler) GetAllEvents(c *fiber.Ctx) error {
Offset: offset,
CountryCode: countryCode,
Featured: isFeatured,
Active: isActive,
Status: eventStatus,
})
// fmt.Printf("League ID: %v", leagueID)
@ -234,22 +270,27 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
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,
// }
// }
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,
}
} else {
firstStartTime = domain.ValidTime{
Value: time.Now(),
Valid: true,
}
}
lastStartTimeQuery := c.Query("last_start_time")
var lastStartTime domain.ValidTime
@ -297,10 +338,7 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
SportID: sportID,
LeagueID: leagueID,
Query: searchString,
FirstStartTime: domain.ValidTime{
Value: time.Now(),
Valid: true,
},
FirstStartTime: firstStartTime,
LastStartTime: lastStartTime,
Limit: limit,
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 {
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 {
}
@ -519,9 +890,9 @@ func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error {
}
type UpdateEventSettingsReq struct {
Featured *bool `json:"is_featured" example:"true"`
IsActive *bool `json:"is_active" example:"true"`
WinningUpperLimit *int `json:"winning_upper_limit" example:"10000"`
Featured *bool `json:"is_featured" example:"true"`
IsActive *bool `json:"is_active" example:"true"`
WinningUpperLimit *int64 `json:"winning_upper_limit" example:"10000"`
}
// UpdateEventSettings godoc
@ -534,8 +905,72 @@ type UpdateEventSettingsReq struct {
// @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]
// @Router /api/v1/events/{id}/settings [put]
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)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
@ -579,12 +1014,12 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
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,
EventID: eventID,
IsFeatured: domain.ConvertBoolPtr(req.Featured),
IsActive: domain.ConvertBoolPtr(req.IsActive),
WinningUpperLimit: domain.ConvertIntPtr(req.WinningUpperLimit),
WinningUpperLimit: domain.ConvertInt64Ptr(req.WinningUpperLimit),
})
if err != nil {

View File

@ -26,7 +26,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
limit := domain.ValidInt64{
Value: int64(pageSize),
Valid: pageSize == 0,
Valid: pageSize != 0,
}
offset := domain.ValidInt64{
Value: int64(page - 1),
@ -54,7 +54,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
sportIDint, err := strconv.ParseInt(sportIDQuery, 10, 64)
if err != nil {
h.BadRequestLogger().Info("invalid sport id",
zap.String("sport_id", sportIDQuery),
@ -86,7 +86,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
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,
IsActive: isActive,
SportID: sportID,
@ -104,7 +104,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
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
@ -156,7 +156,7 @@ func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
sportIDint, err := strconv.ParseInt(sportIDQuery, 10, 64)
if err != nil {
h.BadRequestLogger().Info("invalid sport id",
zap.String("sport_id", sportIDQuery),
@ -235,7 +235,7 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
}
leagueId, err := strconv.Atoi(leagueIdStr)
leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64)
if err != nil {
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{
LeagueID: int64(leagueId),
LeagueID: leagueId,
CompanyID: companyID.Value,
IsActive: domain.ValidBool{
Value: req.IsActive,
@ -308,7 +308,7 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
}
leagueId, err := strconv.Atoi(leagueIdStr)
leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64)
if err != nil {
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{
LeagueID: int64(leagueId),
LeagueID: leagueId,
CompanyID: companyID.Value,
IsFeatured: domain.ValidBool{
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...)
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