feat: refactoring entire system to be multi-tenant

This commit is contained in:
Samuel Tariku 2025-08-22 12:54:48 +03:00
parent 6347984102
commit 6d74cb8c28
77 changed files with 4778 additions and 3110 deletions

View File

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

View File

@ -17,11 +17,14 @@ CREATE TABLE IF NOT EXISTS users (
CHECK ( CHECK (
email IS NOT NULL email IS NOT NULL
OR phone_number IS NOT NULL OR phone_number IS NOT NULL
) ),
UNIQUE(email, company_id),
UNIQUE (phone_number, company_id)
); );
CREATE TABLE IF NOT EXISTS wallets ( CREATE TABLE IF NOT EXISTS wallets (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
balance BIGINT NOT NULL DEFAULT 0, balance BIGINT NOT NULL DEFAULT 0,
currency VARCHAR(3) NOT NULL DEFAULT 'ETB',
is_withdraw BOOLEAN NOT NULL, is_withdraw BOOLEAN NOT NULL,
is_bettable BOOLEAN NOT NULL, is_bettable BOOLEAN NOT NULL,
is_transferable BOOLEAN NOT NULL, is_transferable BOOLEAN NOT NULL,
@ -54,6 +57,7 @@ CREATE TABLE otps (
); );
CREATE TABLE IF NOT EXISTS bets ( CREATE TABLE IF NOT EXISTS bets (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
company_id BIGINT NOT NULL,
amount BIGINT NOT NULL, amount BIGINT NOT NULL,
total_odds REAL NOT NULL, total_odds REAL NOT NULL,
status INT NOT NULL, status INT NOT NULL,
@ -68,6 +72,7 @@ CREATE TABLE IF NOT EXISTS bets (
); );
CREATE TABLE IF NOT EXISTS tickets ( CREATE TABLE IF NOT EXISTS tickets (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
company_id BIGINT NOT NULL,
amount BIGINT NOT NULL, amount BIGINT NOT NULL,
total_odds REAL NOT NULL, total_odds REAL NOT NULL,
IP VARCHAR(255) NOT NULL, IP VARCHAR(255) NOT NULL,
@ -89,14 +94,14 @@ CREATE TABLE IF NOT EXISTS bet_outcomes (
sport_id BIGINT NOT NULL, sport_id BIGINT NOT NULL,
event_id BIGINT NOT null, event_id BIGINT NOT null,
odd_id BIGINT NOT NULL, odd_id BIGINT NOT NULL,
home_team_name VARCHAR(255) NOT NULL, home_team_name TEXT NOT NULL,
away_team_name VARCHAR(255) NOT NULL, away_team_name TEXT NOT NULL,
market_id BIGINT NOT NULL, market_id BIGINT NOT NULL,
market_name VARCHAR(255) NOT NULL, market_name TEXT NOT NULL,
odd REAL NOT NULL, odd REAL NOT NULL,
odd_name VARCHAR(255) NOT NULL, odd_name TEXT NOT NULL,
odd_header VARCHAR(255) NOT NULL, odd_header TEXT NOT NULL,
odd_handicap VARCHAR(255) NOT NULL, odd_handicap TEXT NOT NULL,
status INT NOT NULL DEFAULT 0, status INT NOT NULL DEFAULT 0,
expires TIMESTAMP NOT NULL expires TIMESTAMP NOT NULL
); );
@ -105,14 +110,14 @@ CREATE TABLE IF NOT EXISTS ticket_outcomes (
ticket_id BIGINT NOT NULL, ticket_id BIGINT NOT NULL,
event_id BIGINT NOT null, event_id BIGINT NOT null,
odd_id BIGINT NOT NULL, odd_id BIGINT NOT NULL,
home_team_name VARCHAR(255) NOT NULL, home_team_name TEXT NOT NULL,
away_team_name VARCHAR(255) NOT NULL, away_team_name TEXT NOT NULL,
market_id BIGINT NOT NULL, market_id BIGINT NOT NULL,
market_name VARCHAR(255) NOT NULL, market_name TEXT NOT NULL,
odd REAL NOT NULL, odd REAL NOT NULL,
odd_name VARCHAR(255) NOT NULL, odd_name TEXT NOT NULL,
odd_header VARCHAR(255) NOT NULL, odd_header TEXT NOT NULL,
odd_handicap VARCHAR(255) NOT NULL, odd_handicap TEXT NOT NULL,
status INT NOT NULL DEFAULT 0, status INT NOT NULL DEFAULT 0,
expires TIMESTAMP NOT NULL expires TIMESTAMP NOT NULL
); );
@ -161,7 +166,7 @@ CREATE TABLE IF NOT EXISTS customer_wallets (
CREATE TABLE IF NOT EXISTS wallet_transfer ( CREATE TABLE IF NOT EXISTS wallet_transfer (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
amount BIGINT, amount BIGINT,
message VARCHAR(255) NOT NULL, message TEXT NOT NULL,
type VARCHAR(255), type VARCHAR(255),
receiver_wallet_id BIGINT, receiver_wallet_id BIGINT,
sender_wallet_id BIGINT, sender_wallet_id BIGINT,
@ -255,31 +260,39 @@ CREATE TABLE IF NOT EXISTS branch_locations (
); );
CREATE TABLE events ( CREATE TABLE events (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
sport_id INT, sport_id INT NOT NULL,
match_name TEXT, match_name TEXT NOT NULL,
home_team TEXT, home_team TEXT NOT NULL,
away_team TEXT, away_team TEXT NOT NULL,
home_team_id INT, home_team_id BIGINT NOT NULL,
away_team_id INT, away_team_id BIGINT NOT NULL,
home_kit_image TEXT, home_kit_image TEXT NOT NULL,
away_kit_image TEXT, away_kit_image TEXT NOT NULL,
league_id INT, league_id BIGINT NOT NULL,
league_name TEXT, league_name TEXT NOT NULL,
league_cc TEXT, start_time TIMESTAMP NOT NULL,
start_time TIMESTAMP,
score TEXT, score TEXT,
match_minute INT, match_minute INT,
timer_status TEXT, timer_status TEXT,
added_time INT, added_time INT,
match_period INT, match_period INT,
is_live BOOLEAN, is_live BOOLEAN NOT NULL DEFAULT false,
status TEXT, status TEXT NOT NULL,
fetched_at TIMESTAMP DEFAULT now(), fetched_at TIMESTAMP DEFAULT now(),
source TEXT DEFAULT 'b365api', source TEXT NOT NULL DEFAULT 'b365api' CHECK (
is_featured BOOLEAN NOT NULL DEFAULT FALSE, source IN (
'b365api',
'bfair',
'1xbet',
'bwin',
'enetpulse'
)
),
default_is_active BOOLEAN NOT NULL DEFAULT true,
default_is_featured BOOLEAN NOT NULL DEFAULT false,
default_winning_upper_limit INT NOT NULL,
is_monitored BOOLEAN NOT NULL DEFAULT FALSE, is_monitored BOOLEAN NOT NULL DEFAULT FALSE,
winning_upper_limit INT NOT NULL, UNIQUE(source_event_id, source)
is_active BOOLEAN NOT NULL DEFAULT TRUE
); );
CREATE TABLE event_history ( CREATE TABLE event_history (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
@ -287,52 +300,57 @@ CREATE TABLE event_history (
status TEXT NOT NULL, status TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
CREATE TABLE odds ( CREATE TABLE company_event_settings (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
event_id TEXT, company_id BIGINT NOT NULL,
fi TEXT, event_id TEXT NOT NULL,
is_active BOOLEAN,
is_featured BOOLEAN,
winning_upper_limit INT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(company_id, event_id)
);
CREATE TABLE odds_market (
id BIGSERIAL PRIMARY KEY,
event_id TEXT NOT NULL,
market_type TEXT NOT NULL, market_type TEXT NOT NULL,
market_name TEXT, market_name TEXT NOT NULL,
market_category TEXT, market_category TEXT NOT NULL,
market_id TEXT, market_id TEXT NOT NULL,
name TEXT, raw_odds JSONB NOT NULL,
handicap TEXT, default_is_active BOOLEAN NOT NULL DEFAULT true,
odds_value DOUBLE PRECISION,
section TEXT NOT NULL,
category TEXT,
raw_odds JSONB,
fetched_at TIMESTAMP DEFAULT now(), fetched_at TIMESTAMP DEFAULT now(),
expires_at TIMESTAMP NOT NULL, expires_at TIMESTAMP NOT NULL,
source TEXT DEFAULT 'b365api',
is_active BOOLEAN DEFAULT true,
UNIQUE (market_id, name, handicap), UNIQUE (market_id, name, handicap),
UNIQUE (event_id, market_id, name, handicap), UNIQUE (event_id, market_id, name, handicap),
UNIQUE (event_id, market_id) UNIQUE (event_id, market_id)
); );
CREATE TABLE odd_history ( CREATE TABLE odd_history (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
odd_id BIGINT NOT NULL, odds_market_id BIGINT NOT NULL REFERENCES odds_market(id),
raw_odd_id BIGINT NOT NULL, raw_odd_id BIGINT NOT NULL,
market_id TEXT NOT NULL, market_id TEXT NOT NULL,
event_id TEXT NOT NULL, event_id TEXT NOT NULL,
odd_value DOUBLE PRECISION NOT NULL, odd_value DOUBLE PRECISION NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
CREATE TABLE custom_odd (
id BIGSERIAL PRIMARY KEY,
odd_id BIGINT NOT NULL,
raw_odd_id BIGINT NOT NULL,
event_id TEXT NOT NULL,
odd_value DOUBLE PRECISION NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE disabled_odd ( CREATE TABLE disabled_odd (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
odd_id BIGINT NOT NULL, company_id BIGINT NOT NULL,
odds_market_id BIGINT NOT NULL,
raw_odd_id BIGINT NOT NULL, raw_odd_id BIGINT NOT NULL,
event_id TEXT NOT NULL, event_id TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
CREATE TABLE company_odd_settings (
id BIGSERIAL PRIMARY KEY,
company_id BIGINT NOT NULL,
odds_market_id BIGINT NOT NULL,
is_active BOOLEAN,
custom_raw_odds JSONB,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(company_id, odds_market_id)
);
CREATE TABLE result_log ( CREATE TABLE result_log (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
status_not_finished_count INT NOT NULL, status_not_finished_count INT NOT NULL,
@ -352,6 +370,7 @@ CREATE TABLE result_log (
CREATE TABLE companies ( CREATE TABLE companies (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
admin_id BIGINT NOT NULL, admin_id BIGINT NOT NULL,
wallet_id BIGINT NOT NULL, wallet_id BIGINT NOT NULL,
deducted_percentage REAL NOT NULL, deducted_percentage REAL NOT NULL,
@ -366,19 +385,28 @@ CREATE TABLE companies (
CREATE TABLE leagues ( CREATE TABLE leagues (
id BIGINT PRIMARY KEY, id BIGINT PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
img TEXT, img_url TEXT,
country_code TEXT, country_code TEXT,
bet365_id INT, bet365_id INT,
sport_id INT NOT NULL, sport_id INT NOT NULL,
is_active BOOLEAN DEFAULT true, default_is_active BOOLEAN NOT NULL DEFAULT true,
is_featured BOOLEAN DEFAULT false default_is_featured BOOLEAN NOT NULL DEFAULT false
);
CREATE TABLE company_league_settings (
id BIGSERIAL PRIMARY KEY,
company_id BIGINT NOT NULL,
league_id BIGINT NOT NULL,
is_active BOOLEAN,
is_featured BOOLEAN,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(league_id, company_id)
); );
CREATE TABLE teams ( CREATE TABLE teams (
id TEXT PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
team_name TEXT NOT NULL, team_name TEXT NOT NULL,
country TEXT, country_code TEXT NOT NULL,
bet365_id INT, bet365_id BIGINT,
logo_url TEXT img_url TEXT
); );
CREATE TABLE IF NOT EXISTS settings ( CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY, key TEXT PRIMARY KEY,
@ -387,14 +415,14 @@ CREATE TABLE IF NOT EXISTS settings (
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
CREATE TABLE bonus ( CREATE TABLE bonus (
id BIGSERIAL PRIMARY KEY,
multiplier REAL NOT NULL, multiplier REAL NOT NULL,
id BIGSERIAL PRIMARY KEY,
balance_cap BIGINT NOT NULL DEFAULT 0 balance_cap BIGINT NOT NULL DEFAULT 0
); );
CREATE TABLE flags ( CREATE TABLE flags (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
bet_id BIGINT REFERENCES bets(id) ON DELETE CASCADE, bet_id BIGINT REFERENCES bets(id) ON DELETE CASCADE,
odd_id BIGINT REFERENCES odds(id), odds_market_id BIGINT REFERENCES odds_market(id),
reason TEXT, reason TEXT,
flagged_at TIMESTAMP DEFAULT NOW(), flagged_at TIMESTAMP DEFAULT NOW(),
resolved BOOLEAN DEFAULT FALSE, resolved BOOLEAN DEFAULT FALSE,
@ -544,17 +572,56 @@ SELECT sd.*,
st.verified AS transaction_verified st.verified AS transaction_verified
FROM shop_deposits AS sd FROM shop_deposits AS sd
JOIN shop_transactions st ON st.id = sd.shop_transaction_id; JOIN shop_transactions st ON st.id = sd.shop_transaction_id;
CREATE VIEW league_with_settings AS
SELECT l.*,
cls.company_id,
COALESCE(cls.is_active, l.default_is_active) AS is_active,
COALESCE(cls.is_featured, l.default_is_featured) AS is_featured,
cls.updated_at
FROM leagues l
JOIN company_league_settings cls ON leagues.id = cls.league_id;
CREATE VIEW event_with_settings AS
SELECT e.*,
ces.company_id,
COALESCE(ces.is_active, e.default_is_active) AS is_active,
COALESCE(ces.is_featured, e.default_is_featured) AS is_featured,
COALESCE(
ces.winning_upper_limit,
e.default_winning_upper_limit
) AS winning_upper_limit,
ces.updated_at,
l.country_code as league_cc
FROM events e
JOIN company_event_settings ces ON events.id = ces.event_id
JOIN leagues l ON leagues.id = e.league_id;
CREATE VIEW event_with_country AS
SELECT events.*,
leagues.country_code as league_cc
FROM events
LEFT JOIN leagues ON leagues.id = league_id;
CREATE VIEW odds_market_with_settings AS
SELECT o.*,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
cos.updated_at
FROM odds_market o
JOIN company_odd_settings cos ON o.id = cos.odds_market_id;
CREATE VIEW odds_market_with_event AS
SELECT o.*,
e.is_monitored,
e.is_live,
e.status,
e.source
FROM odds_market o
JOIN events e ON o.event_id = e.id;
-- Foreign Keys -- Foreign Keys
ALTER TABLE users
ADD CONSTRAINT unique_email UNIQUE (email),
ADD CONSTRAINT unique_phone_number UNIQUE (phone_number);
ALTER TABLE refresh_tokens ALTER TABLE refresh_tokens
ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id); ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id);
ALTER TABLE bets ALTER TABLE bets
ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users(id); ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users(id);
ALTER TABLE wallets ALTER TABLE wallets
ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users(id), ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users(id);
ADD COLUMN currency VARCHAR(3) NOT NULL DEFAULT 'ETB';
ALTER TABLE customer_wallets ALTER TABLE customer_wallets
ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users(id), ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users(id),
ADD CONSTRAINT fk_customer_wallets_regular_wallet FOREIGN KEY (regular_wallet_id) REFERENCES wallets(id), ADD CONSTRAINT fk_customer_wallets_regular_wallet FOREIGN KEY (regular_wallet_id) REFERENCES wallets(id),
@ -585,3 +652,6 @@ ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(i
ALTER TABLE companies ALTER TABLE companies
ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id), ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id),
ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE; ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE;
ALTER TABLE company_league_settings
ADD CONSTRAINT fk_league_settings_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_league_settings_league FOREIGN KEY (league_id) REFERENCES league_id(id) ON DELETE CASCADE;

View File

@ -1,12 +1,17 @@
-- name: GetUserByEmailPhone :one -- name: GetUserByEmailPhone :one
SELECT * SELECT *
FROM users FROM users
WHERE email = $1 WHERE (
OR phone_number = $2; email = $1
OR phone_number = $2
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
);
-- name: CreateRefreshToken :exec -- name: CreateRefreshToken :exec
INSERT INTO refresh_tokens (user_id, token, expires_at, created_at, revoked) INSERT INTO refresh_tokens (user_id, token, expires_at, created_at, revoked)
VALUES ($1, $2, $3, $4, $5); VALUES ($1, $2, $3, $4, $5);
-- name: GetRefreshToken :one -- name: GetRefreshToken :one
SELECT * SELECT *
FROM refresh_tokens FROM refresh_tokens

View File

@ -1,11 +1,12 @@
-- name: CreateCompany :one -- name: CreateCompany :one
INSERT INTO companies ( INSERT INTO companies (
name, name,
slug,
admin_id, admin_id,
wallet_id, wallet_id,
deducted_percentage deducted_percentage
) )
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4, $5)
RETURNING *; RETURNING *;
-- name: GetAllCompanies :many -- name: GetAllCompanies :many
SELECT * SELECT *
@ -29,6 +30,10 @@ WHERE (
SELECT * SELECT *
FROM companies_details FROM companies_details
WHERE id = $1; WHERE id = $1;
-- name: GetCompanyIDUsingSlug :one
SELECT id
FROM companies
WHERE slug = $1;
-- name: SearchCompanyByName :many -- name: SearchCompanyByName :many
SELECT * SELECT *
FROM companies_details FROM companies_details

View File

@ -1,26 +1,35 @@
-- name: InsertCustomOdd :one -- -- name: InsertCustomOddsMarket :one
INSERT INTO custom_odd ( -- INSERT INTO custom_odds_market (
odd_id, -- odds_market_id,
raw_odd_id, -- company_id,
event_id, -- event_id,
odd_value -- raw_odds
) -- )
VALUES ($1, $2, $3, $4) -- VALUES ($1, $2, $3, $4)
RETURNING *; -- RETURNING *;
-- name: GetAllCustomOdds :many -- -- name: GetAllCustomOdds :many
SELECT * -- SELECT *
FROM custom_odd; -- FROM custom_odds_market
-- name: GetCustomOddByRawOddID :one -- WHERE (
SELECT * -- company_id = sqlc.narg('company_id')
FROM custom_odd -- OR sqlc.narg('company_id') IS NULL
WHERE raw_odd_id = $1; -- );
-- name: GetCustomOddByID :one -- -- name: GetCustomOddByID :one
SELECT * -- SELECT *
FROM custom_odd -- FROM custom_odds_market
WHERE id = $1; -- WHERE id = $1;
-- name: DeleteCustomOddsByID :exec -- -- name: GetCustomOddByOddID :one
DELETE FROM disabled_odd -- SELECT *
WHERE raw_odd_id = $1; -- FROM custom_odds_market
-- name: DeleteCustomOddsByRawOddID :exec -- WHERE odds_market_id = $1
DELETE FROM disabled_odd -- AND company_id = $2;
WHERE raw_odd_id = $1; -- -- name: DeleteCustomOddsByID :exec
-- DELETE FROM custom_odds_market
-- WHERE id = $1;
-- -- name: DeleteCustomOddsByOddID :exec
-- DELETE FROM custom_odds_market
-- WHERE odds_market_id = $1
-- AND company_id = $2;
-- -- name: DeleteCustomOddByEventID :exec
-- DELETE FROM custom_odds_market
-- WHERE event_id = $1;

View File

@ -1,10 +1,11 @@
-- name: InsertDisabledOdds :one -- name: InsertDisabledOdds :one
INSERT INTO disabled_odd ( INSERT INTO disabled_odd (
odd_id, odds_market_id,
company_id,
event_id, event_id,
raw_odd_id raw_odd_id
) )
VALUES ($1, $2, $3) VALUES ($1, $2, $3, $4)
RETURNING *; RETURNING *;
-- name: GetAllDisabledOdds :many -- name: GetAllDisabledOdds :many
SELECT * SELECT *

View File

@ -11,13 +11,7 @@ INSERT INTO events (
away_kit_image, away_kit_image,
league_id, league_id,
league_name, league_name,
league_cc,
start_time, start_time,
score,
match_minute,
timer_status,
added_time,
match_period,
is_live, is_live,
status, status,
source source
@ -37,13 +31,7 @@ VALUES (
$12, $12,
$13, $13,
$14, $14,
$15, $15
$16,
$17,
$18,
$19,
$20,
$21
) ON CONFLICT (id) DO ) ON CONFLICT (id) DO
UPDATE UPDATE
SET sport_id = EXCLUDED.sport_id, SET sport_id = EXCLUDED.sport_id,
@ -64,79 +52,35 @@ SET sport_id = EXCLUDED.sport_id,
added_time = EXCLUDED.added_time, added_time = EXCLUDED.added_time,
match_period = EXCLUDED.match_period, match_period = EXCLUDED.match_period,
is_live = EXCLUDED.is_live, is_live = EXCLUDED.is_live,
status = EXCLUDED.status,
source = EXCLUDED.source, source = EXCLUDED.source,
fetched_at = now(); fetched_at = now();
-- name: InsertUpcomingEvent :exec -- name: InsertEventSettings :exec
INSERT INTO events ( INSERT INTO company_event_settings (
id, company_id,
sport_id, event_id,
match_name, is_active,
home_team, is_featured,
away_team, winning_upper_limit
home_team_id,
away_team_id,
home_kit_image,
away_kit_image,
league_id,
league_name,
league_cc,
start_time,
is_live,
status,
source
) )
VALUES ( VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12,
$13,
false,
'upcoming',
$14
) ON CONFLICT (id) DO
UPDATE UPDATE
SET sport_id = EXCLUDED.sport_id, SET is_active = EXCLUDED.is_active,
match_name = EXCLUDED.match_name, is_featured = EXCLUDED.is_featured,
home_team = EXCLUDED.home_team, winning_upper_limit = EXCLUDED.winning_upper_limit;
away_team = EXCLUDED.away_team,
home_team_id = EXCLUDED.home_team_id,
away_team_id = EXCLUDED.away_team_id,
home_kit_image = EXCLUDED.home_kit_image,
away_kit_image = EXCLUDED.away_kit_image,
league_id = EXCLUDED.league_id,
league_name = EXCLUDED.league_name,
league_cc = EXCLUDED.league_cc,
start_time = EXCLUDED.start_time,
is_live = false,
status = 'upcoming',
source = EXCLUDED.source,
fetched_at = now();
-- name: ListLiveEvents :many -- name: ListLiveEvents :many
SELECT id SELECT id
FROM events FROM event_with_country
WHERE is_live = true; WHERE is_live = true;
-- name: GetAllUpcomingEvents :many -- name: GetAllUpcomingEvents :many
SELECT * SELECT *
FROM events FROM event_with_country
WHERE start_time > now() WHERE start_time > now()
AND is_live = false AND is_live = false
AND status = 'upcoming' AND status = 'upcoming'
ORDER BY start_time ASC; ORDER BY start_time ASC;
-- name: GetExpiredUpcomingEvents :many -- name: GetExpiredEvents :many
SELECT events.*, SELECT *
leagues.country_code as league_cc FROM event_with_country
FROM events
LEFT JOIN leagues ON leagues.id = league_id
WHERE start_time < now() WHERE start_time < now()
and ( and (
status = sqlc.narg('status') status = sqlc.narg('status')
@ -145,8 +89,7 @@ WHERE start_time < now()
ORDER BY start_time ASC; ORDER BY start_time ASC;
-- name: GetTotalEvents :one -- name: GetTotalEvents :one
SELECT COUNT(*) SELECT COUNT(*)
FROM events FROM event_with_country
LEFT JOIN leagues ON leagues.id = league_id
WHERE is_live = false WHERE is_live = false
AND status = 'upcoming' AND status = 'upcoming'
AND ( AND (
@ -154,7 +97,7 @@ WHERE is_live = false
OR sqlc.narg('league_id') IS NULL OR sqlc.narg('league_id') IS NULL
) )
AND ( AND (
events.sport_id = sqlc.narg('sport_id') sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL OR sqlc.narg('sport_id') IS NULL
) )
AND ( AND (
@ -171,18 +114,12 @@ WHERE is_live = false
OR sqlc.narg('first_start_time') IS NULL OR sqlc.narg('first_start_time') IS NULL
) )
AND ( AND (
leagues.country_code = sqlc.narg('country_code') league_cc = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL OR sqlc.narg('country_code') IS NULL
)
AND (
events.is_featured = sqlc.narg('is_featured')
OR sqlc.narg('is_featured') IS NULL
); );
-- name: GetPaginatedUpcomingEvents :many -- name: GetPaginatedUpcomingEvents :many
SELECT events.*, SELECT *
leagues.country_code as league_cc FROM event_with_country
FROM events
LEFT JOIN leagues ON leagues.id = league_id
WHERE start_time > now() WHERE start_time > now()
AND is_live = false AND is_live = false
AND status = 'upcoming' AND status = 'upcoming'
@ -191,7 +128,7 @@ WHERE start_time > now()
OR sqlc.narg('league_id') IS NULL OR sqlc.narg('league_id') IS NULL
) )
AND ( AND (
events.sport_id = sqlc.narg('sport_id') sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL OR sqlc.narg('sport_id') IS NULL
) )
AND ( AND (
@ -208,31 +145,96 @@ WHERE start_time > now()
OR sqlc.narg('first_start_time') IS NULL OR sqlc.narg('first_start_time') IS NULL
) )
AND ( AND (
leagues.country_code = sqlc.narg('country_code') league_cc = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL OR sqlc.narg('country_code') IS NULL
) )
ORDER BY start_time ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetTotalCompanyEvents :one
SELECT COUNT(*)
FROM event_with_settings
WHERE company_id = $1
AND is_live = false
AND status = 'upcoming'
AND ( AND (
events.is_featured = sqlc.narg('is_featured') league_id = sqlc.narg('league_id')
OR sqlc.narg('is_featured') IS NULL OR sqlc.narg('league_id') IS NULL
)
AND (
sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL
)
AND (
match_name ILIKE '%' || sqlc.narg('query') || '%'
OR league_name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
start_time < sqlc.narg('last_start_time')
OR sqlc.narg('last_start_time') IS NULL
)
AND (
start_time > sqlc.narg('first_start_time')
OR sqlc.narg('first_start_time') IS NULL
)
AND (
league_cc = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL
);
-- name: GetEventsWithSettings :many
SELECT *
FROM event_with_settings
WHERE company_id = $1
AND start_time > now()
AND is_live = false
AND status = 'upcoming'
AND (
league_id = sqlc.narg('league_id')
OR sqlc.narg('league_id') IS NULL
)
AND (
sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL
)
AND (
match_name ILIKE '%' || sqlc.narg('query') || '%'
OR league_name ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
start_time < sqlc.narg('last_start_time')
OR sqlc.narg('last_start_time') IS NULL
)
AND (
start_time > sqlc.narg('first_start_time')
OR sqlc.narg('first_start_time') IS NULL
)
AND (
league_cc = sqlc.narg('country_code')
OR sqlc.narg('country_code') IS NULL
) )
ORDER BY start_time ASC ORDER BY start_time ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetUpcomingByID :one -- name: GetUpcomingByID :one
SELECT * SELECT *
FROM events FROM event_with_country
WHERE id = $1 WHERE id = $1
AND is_live = false AND is_live = false
AND status = 'upcoming' AND status = 'upcoming'
LIMIT 1; LIMIT 1;
-- name: GetEventWithSettingByID :one
SELECT *
FROM event_with_settings
WHERE id = $1
AND company_id = $2
AND is_live = false
AND status = 'upcoming'
LIMIT 1;
-- name: UpdateMatchResult :exec -- name: UpdateMatchResult :exec
UPDATE events UPDATE events
SET score = $1, SET score = $1,
status = $2 status = $2
WHERE id = $3; WHERE id = $3;
-- name: UpdateEventFeatured :exec
UPDATE events
SET is_featured = $1
WHERE id = $2;
-- name: IsEventMonitored :one -- name: IsEventMonitored :one
SELECT is_monitored SELECT is_monitored
FROM events FROM events
@ -241,6 +243,19 @@ WHERE id = $1;
UPDATE events UPDATE events
SET is_monitored = $1 SET is_monitored = $1
WHERE id = $2; WHERE id = $2;
-- name: UpdateEventSettings :exec
UPDATE company_event_settings
SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured = COALESCE(
sqlc.narg('is_featured'),
is_featured
),
winning_upper_limit = COALESCE(
sqlc.narg('winning_upper_limit'),
winning_upper_limit
)
WHERE event_id = $1
AND company_id = $2;
-- name: DeleteEvent :exec -- name: DeleteEvent :exec
DELETE FROM events DELETE FROM events
WHERE id = $1; WHERE id = $1;

View File

@ -4,20 +4,11 @@ SELECT DATE_TRUNC('month', start_time) AS month,
FROM events FROM events
JOIN leagues ON leagues.id = events.league_id JOIN leagues ON leagues.id = events.league_id
WHERE ( WHERE (
events.is_featured = sqlc.narg('is_event_featured')
OR sqlc.narg('is_event_featured') IS NULL
)
AND (
leagues.is_featured = sqlc.narg('is_league_featured')
OR sqlc.narg('is_league_featured') IS NULL
)
AND (
events.league_id = sqlc.narg('league_id') events.league_id = sqlc.narg('league_id')
OR sqlc.narg('league_id') IS NULL OR sqlc.narg('league_id') IS NULL
) )
GROUP BY month GROUP BY month
ORDER BY month; ORDER BY month;
-- name: GetLeagueEventStat :many -- name: GetLeagueEventStat :many
SELECT leagues.id, SELECT leagues.id,
leagues.name, leagues.name,
@ -63,9 +54,5 @@ SELECT leagues.id,
) AS removed ) AS removed
FROM leagues FROM leagues
JOIN events ON leagues.id = events.league_id JOIN events ON leagues.id = events.league_id
WHERE (
leagues.is_featured = sqlc.narg('is_league_featured')
OR sqlc.narg('is_league_featured') IS NULL
)
GROUP BY leagues.id, GROUP BY leagues.id,
leagues.name; leagues.name;

View File

@ -1,7 +1,7 @@
-- name: CreateFlag :one -- name: CreateFlag :one
INSERT INTO flags ( INSERT INTO flags (
bet_id, bet_id,
odd_id, odds_market_id,
reason reason
) VALUES ( ) VALUES (
$1, $2, $3 $1, $2, $3

View File

@ -5,25 +5,28 @@ INSERT INTO leagues (
country_code, country_code,
bet365_id, bet365_id,
sport_id, sport_id,
is_active, default_is_active,
is_featured default_is_featured
) )
VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO
UPDATE UPDATE
SET name = EXCLUDED.name, SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code, country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id, bet365_id = EXCLUDED.bet365_id,
is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured,
sport_id = EXCLUDED.sport_id; sport_id = EXCLUDED.sport_id;
-- name: GetAllLeagues :many -- name: InsertLeagueSettings :exec
SELECT id, INSERT INTO company_league_settings (
name, company_id,
country_code, league_id,
bet365_id,
is_active, is_active,
is_featured, is_featured
sport_id )
VALUES ($1, $2, $3, $4) ON CONFLICT(company_id, league_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured;
-- name: GetAllLeagues :many
SELECT *
FROM leagues FROM leagues
WHERE ( WHERE (
country_code = sqlc.narg('country_code') country_code = sqlc.narg('country_code')
@ -33,6 +36,20 @@ WHERE (
sport_id = sqlc.narg('sport_id') sport_id = sqlc.narg('sport_id')
OR sqlc.narg('sport_id') IS NULL OR sqlc.narg('sport_id') IS NULL
) )
ORDER BY name ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetAllLeaguesWithSettings :many
SELECT *
FROM league_with_settings
WHERE (company_id = $1)
AND (
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 ( AND (
is_active = sqlc.narg('is_active') is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') IS NULL OR sqlc.narg('is_active') IS NULL
@ -44,21 +61,12 @@ WHERE (
ORDER BY is_featured DESC, ORDER BY is_featured DESC,
name ASC name ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetFeaturedLeagues :many
SELECT id,
name,
country_code,
bet365_id,
is_active,
is_featured,
sport_id
FROM leagues
WHERE is_featured = true;
-- name: CheckLeagueSupport :one -- name: CheckLeagueSupport :one
SELECT EXISTS( SELECT EXISTS(
SELECT 1 SELECT 1
FROM leagues FROM company_league_settings
WHERE id = $1 WHERE league_id = $1
AND company_id = $2
AND is_active = true AND is_active = true
); );
-- name: UpdateLeague :exec -- name: UpdateLeague :exec
@ -66,20 +74,14 @@ UPDATE leagues
SET name = COALESCE(sqlc.narg('name'), name), SET name = COALESCE(sqlc.narg('name'), name),
country_code = COALESCE(sqlc.narg('country_code'), country_code), country_code = COALESCE(sqlc.narg('country_code'), country_code),
bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id), bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id),
is_active = COALESCE(sqlc.narg('is_active'), is_active),
is_featured = COALESCE(sqlc.narg('is_featured'), is_featured),
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id) sport_id = COALESCE(sqlc.narg('sport_id'), sport_id)
WHERE id = $1; WHERE id = $1;
-- name: UpdateLeagueByBet365ID :exec -- name: UpdateLeagueSettings :exec
UPDATE leagues UPDATE company_league_settings
SET name = COALESCE(sqlc.narg('name'), name), SET is_active = COALESCE(sqlc.narg('is_active'), is_active),
id = COALESCE(sqlc.narg('id'), id), is_featured = COALESCE(
country_code = COALESCE(sqlc.narg('country_code'), country_code), sqlc.narg('is_featured'),
is_active = COALESCE(sqlc.narg('is_active'), is_active), is_featured
is_featured = COALESCE(sqlc.narg('is_featured'), is_featured), )
sport_id = COALESCE(sqlc.narg('sport_id'), sport_id) WHERE league_id = $1
WHERE bet365_id = $1; AND company_id = $2;
-- name: SetLeagueActive :exec
UPDATE leagues
SET is_active = $2
WHERE id = $1;

View File

@ -1,6 +1,6 @@
-- name: InsertOddHistory :one -- name: InsertOddHistory :one
INSERT INTO odd_history ( INSERT INTO odd_history (
odd_id, odds_market_id,
market_id, market_id,
raw_odd_id, raw_odd_id,
event_id, event_id,
@ -12,7 +12,7 @@ RETURNING *;
SELECT * SELECT *
FROM odd_history FROM odd_history
WHERE ( WHERE (
odd_id = sqlc.narg('odd_id') odds_market_id = sqlc.narg('odd_id')
OR sqlc.narg('odd_id') IS NULL OR sqlc.narg('odd_id') IS NULL
) )
AND ( AND (
@ -39,7 +39,7 @@ WHERE (
SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) * SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) *
FROM odd_history FROM odd_history
WHERE ( WHERE (
odd_id = sqlc.narg('odd_id') odds_market_id = sqlc.narg('odd_id')
OR sqlc.narg('odd_id') IS NULL OR sqlc.narg('odd_id') IS NULL
) )
AND ( AND (

View File

@ -1,19 +1,11 @@
-- name: InsertNonLiveOdd :exec -- name: InsertOddsMarket :exec
INSERT INTO odds ( INSERT INTO odds_market (
event_id, event_id,
fi,
market_type, market_type,
market_name, market_name,
market_category, market_category,
market_id, market_id,
name,
handicap,
odds_value,
section,
category,
raw_odds, raw_odds,
is_active,
source,
fetched_at, fetched_at,
expires_at expires_at
) )
@ -25,64 +17,69 @@ VALUES (
$5, $5,
$6, $6,
$7, $7,
$8, $8
$9,
$10,
$11,
$12,
$13,
$14,
$15,
$16
) ON CONFLICT (event_id, market_id) DO ) ON CONFLICT (event_id, market_id) DO
UPDATE UPDATE
SET odds_value = EXCLUDED.odds_value, SET market_type = EXCLUDED.market_type,
raw_odds = EXCLUDED.raw_odds,
market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name, market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category, market_category = EXCLUDED.market_category,
name = EXCLUDED.name, raw_odds = EXCLUDED.raw_odds,
handicap = EXCLUDED.handicap,
fetched_at = EXCLUDED.fetched_at, fetched_at = EXCLUDED.fetched_at,
is_active = EXCLUDED.is_active, expires_at = EXCLUDED.expires_at;
source = EXCLUDED.source, -- name: InsertOddSettings :exec
fi = EXCLUDED.fi; INSERT INTO company_odd_settings (
-- name: GetPrematchOdds :many company_id,
odds_market_id,
is_active,
custom_raw_odds
)
VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
custom_raw_odds = EXCLUDED.custom_raw_odds;
-- name: GetAllOdds :many
SELECT * SELECT *
FROM odds FROM odds_market_with_event
WHERE is_active = true LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
AND source = 'bet365'; -- name: GetAllOddsWithSettings :many
-- name: GetALLPrematchOdds :many
SELECT * SELECT *
FROM odds FROM odds_market_with_settings
WHERE is_active = true WHERE company_id = $1
AND source = 'bet365'; LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetOddsByMarketID :one -- name: GetOddsByMarketID :one
SELECT * SELECT *
FROM odds FROM odds_market_with_event
WHERE market_id = $1 WHERE market_id = $1
AND fi = $2 AND event_id = $2;
AND is_active = true -- name: GetOddsWithSettingsByMarketID :one
AND source = 'bet365'; SELECT *
-- name: GetPrematchOddsByUpcomingID :many FROM odds_market_with_settings
SELECT o.* WHERE market_id = $1
FROM odds o AND event_id = $2
JOIN events e ON o.fi = e.id AND company_id = $3;
WHERE e.id = $1 -- name: GetOddsByEventID :many
AND e.is_live = false SELECT *
AND e.status = 'upcoming' FROM odds_market_with_event
AND o.is_active = true WHERE event_id = $1
AND o.source = 'bet365'; AND (
-- name: GetPaginatedPrematchOddsByUpcomingID :many is_live = sqlc.narg('is_live')
SELECT o.* OR sqlc.narg('is_live') IS NULL
FROM odds o )
JOIN events e ON o.fi = e.id AND (
WHERE e.id = $1 status = sqlc.narg('status')
AND e.is_live = false OR sqlc.narg('status') IS NULL
AND e.status = 'upcoming' )
AND o.is_active = true AND (
AND o.source = 'bet365' source = sqlc.narg('source')
OR sqlc.narg('source') IS NULL
)
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetOddsWithSettingsByEventID :many
SELECT *
FROM odds_market_with_settings
WHERE event_id = $1
AND company_id = $2
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: DeleteOddsForEvent :exec -- name: DeleteOddsForEvent :exec
DELETE FROM odds DELETE FROM odds_market
Where fi = $1; Where event_id = $1;

View File

@ -107,18 +107,15 @@ SELECT id,
suspended_at, suspended_at,
company_id company_id
FROM users FROM users
WHERE ( WHERE (company_id = $1)
first_name ILIKE '%' || $1 || '%' AND (
OR last_name ILIKE '%' || $1 || '%' first_name ILIKE '%' || $2 || '%'
OR phone_number LIKE '%' || $1 || '%' OR last_name ILIKE '%' || $2 || '%'
OR phone_number LIKE '%' || $2 || '%'
) )
AND ( AND (
role = sqlc.narg('role') role = sqlc.narg('role')
OR sqlc.narg('role') IS NULL OR sqlc.narg('role') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
); );
-- name: UpdateUser :exec -- name: UpdateUser :exec
UPDATE users UPDATE users
@ -146,12 +143,14 @@ SELECT EXISTS (
FROM users FROM users
WHERE users.phone_number = $1 WHERE users.phone_number = $1
AND users.phone_number IS NOT NULL AND users.phone_number IS NOT NULL
AND users.company_id = $2
) AS phone_exists, ) AS phone_exists,
EXISTS ( EXISTS (
SELECT 1 SELECT 1
FROM users FROM users
WHERE users.email = $2 WHERE users.email = $3
AND users.email IS NOT NULL AND users.email IS NOT NULL
AND users.company_id = $2
) AS email_exists; ) AS email_exists;
-- name: GetUserByEmail :one -- name: GetUserByEmail :one
SELECT id, SELECT id,
@ -168,7 +167,8 @@ SELECT id,
suspended_at, suspended_at,
company_id company_id
FROM users FROM users
WHERE email = $1; WHERE email = $1
AND company_id = $2;
-- name: GetUserByPhone :one -- name: GetUserByPhone :one
SELECT id, SELECT id,
first_name, first_name,
@ -184,7 +184,8 @@ SELECT id,
suspended_at, suspended_at,
company_id company_id
FROM users FROM users
WHERE phone_number = $1; WHERE phone_number = $1
AND company_id = $2;
-- name: UpdatePassword :exec -- name: UpdatePassword :exec
UPDATE users UPDATE users
SET password = $1, SET password = $1,
@ -192,6 +193,7 @@ SET password = $1,
WHERE ( WHERE (
email = $2 email = $2
OR phone_number = $3 OR phone_number = $3
AND company_id = $4
); );
-- name: GetAdminByCompanyID :one -- name: GetAdminByCompanyID :one
SELECT users.* SELECT users.*

View File

@ -78,17 +78,24 @@ func (q *Queries) GetRefreshTokenByUserID(ctx context.Context, userID int64) (Re
const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by
FROM users FROM users
WHERE email = $1 WHERE (
email = $1
OR phone_number = $2 OR phone_number = $2
)
AND (
company_id = $3
OR $3 IS NULL
)
` `
type GetUserByEmailPhoneParams struct { type GetUserByEmailPhoneParams struct {
Email pgtype.Text `json:"email"` Email pgtype.Text `json:"email"`
PhoneNumber pgtype.Text `json:"phone_number"` PhoneNumber pgtype.Text `json:"phone_number"`
CompanyID pgtype.Int8 `json:"company_id"`
} }
func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPhoneParams) (User, error) { func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPhoneParams) (User, error) {
row := q.db.QueryRow(ctx, GetUserByEmailPhone, arg.Email, arg.PhoneNumber) row := q.db.QueryRow(ctx, GetUserByEmailPhone, arg.Email, arg.PhoneNumber, arg.CompanyID)
var i User var i User
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,

View File

@ -22,7 +22,7 @@ INSERT INTO bets (
fast_code fast_code
) )
VALUES ($1, $2, $3, $4, $5, $6, $7) VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at RETURNING id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at
` `
type CreateBetParams struct { type CreateBetParams struct {
@ -48,6 +48,7 @@ func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, erro
var i Bet var i Bet
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Status, &i.Status,
@ -100,7 +101,7 @@ func (q *Queries) DeleteBetOutcome(ctx context.Context, betID int64) error {
} }
const GetAllBets = `-- name: GetAllBets :many const GetAllBets = `-- name: GetAllBets :many
SELECT 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
FROM bet_with_outcomes FROM bet_with_outcomes
wHERE ( wHERE (
user_id = $1 user_id = $1
@ -156,6 +157,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
var i BetWithOutcome var i BetWithOutcome
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Status, &i.Status,
@ -182,7 +184,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
} }
const GetBetByFastCode = `-- name: GetBetByFastCode :one const GetBetByFastCode = `-- name: GetBetByFastCode :one
SELECT 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
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE fast_code = $1 WHERE fast_code = $1
LIMIT 1 LIMIT 1
@ -193,6 +195,7 @@ func (q *Queries) GetBetByFastCode(ctx context.Context, fastCode string) (BetWit
var i BetWithOutcome var i BetWithOutcome
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Status, &i.Status,
@ -212,7 +215,7 @@ func (q *Queries) GetBetByFastCode(ctx context.Context, fastCode string) (BetWit
} }
const GetBetByID = `-- name: GetBetByID :one const GetBetByID = `-- name: GetBetByID :one
SELECT 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
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE id = $1 WHERE id = $1
` `
@ -222,6 +225,7 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err
var i BetWithOutcome var i BetWithOutcome
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Status, &i.Status,
@ -241,7 +245,7 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err
} }
const GetBetByUserID = `-- name: GetBetByUserID :many const GetBetByUserID = `-- name: GetBetByUserID :many
SELECT 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
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE user_id = $1 WHERE user_id = $1
` `
@ -257,6 +261,7 @@ func (q *Queries) GetBetByUserID(ctx context.Context, userID int64) ([]BetWithOu
var i BetWithOutcome var i BetWithOutcome
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Status, &i.Status,
@ -424,7 +429,7 @@ func (q *Queries) GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (i
} }
const GetBetsForCashback = `-- name: GetBetsForCashback :many const GetBetsForCashback = `-- name: GetBetsForCashback :many
SELECT 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
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE status = 2 WHERE status = 2
AND processed = false AND processed = false
@ -441,6 +446,7 @@ func (q *Queries) GetBetsForCashback(ctx context.Context) ([]BetWithOutcome, err
var i BetWithOutcome var i BetWithOutcome
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Status, &i.Status,

View File

@ -14,16 +14,18 @@ import (
const CreateCompany = `-- name: CreateCompany :one const CreateCompany = `-- name: CreateCompany :one
INSERT INTO companies ( INSERT INTO companies (
name, name,
slug,
admin_id, admin_id,
wallet_id, wallet_id,
deducted_percentage deducted_percentage
) )
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4, $5)
RETURNING id, name, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at RETURNING id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at
` `
type CreateCompanyParams struct { type CreateCompanyParams struct {
Name string `json:"name"` Name string `json:"name"`
Slug string `json:"slug"`
AdminID int64 `json:"admin_id"` AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"` WalletID int64 `json:"wallet_id"`
DeductedPercentage float32 `json:"deducted_percentage"` DeductedPercentage float32 `json:"deducted_percentage"`
@ -32,6 +34,7 @@ type CreateCompanyParams struct {
func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (Company, error) { func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (Company, error) {
row := q.db.QueryRow(ctx, CreateCompany, row := q.db.QueryRow(ctx, CreateCompany,
arg.Name, arg.Name,
arg.Slug,
arg.AdminID, arg.AdminID,
arg.WalletID, arg.WalletID,
arg.DeductedPercentage, arg.DeductedPercentage,
@ -40,6 +43,7 @@ func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (C
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Slug,
&i.AdminID, &i.AdminID,
&i.WalletID, &i.WalletID,
&i.DeductedPercentage, &i.DeductedPercentage,
@ -61,7 +65,7 @@ func (q *Queries) DeleteCompany(ctx context.Context, id int64) error {
} }
const GetAllCompanies = `-- name: GetAllCompanies :many const GetAllCompanies = `-- name: GetAllCompanies :many
SELECT id, name, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_details FROM companies_details
WHERE ( WHERE (
name ILIKE '%' || $1 || '%' name ILIKE '%' || $1 || '%'
@ -98,6 +102,7 @@ func (q *Queries) GetAllCompanies(ctx context.Context, arg GetAllCompaniesParams
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Slug,
&i.AdminID, &i.AdminID,
&i.WalletID, &i.WalletID,
&i.DeductedPercentage, &i.DeductedPercentage,
@ -121,7 +126,7 @@ func (q *Queries) GetAllCompanies(ctx context.Context, arg GetAllCompaniesParams
} }
const GetCompanyByID = `-- name: GetCompanyByID :one const GetCompanyByID = `-- name: GetCompanyByID :one
SELECT id, name, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_details FROM companies_details
WHERE id = $1 WHERE id = $1
` `
@ -132,6 +137,7 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Slug,
&i.AdminID, &i.AdminID,
&i.WalletID, &i.WalletID,
&i.DeductedPercentage, &i.DeductedPercentage,
@ -147,8 +153,21 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail
return i, err return i, err
} }
const GetCompanyIDUsingSlug = `-- name: GetCompanyIDUsingSlug :one
SELECT id
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
}
const SearchCompanyByName = `-- name: SearchCompanyByName :many const SearchCompanyByName = `-- name: SearchCompanyByName :many
SELECT id, name, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_details FROM companies_details
WHERE name ILIKE '%' || $1 || '%' WHERE name ILIKE '%' || $1 || '%'
` `
@ -165,6 +184,7 @@ func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text)
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Slug,
&i.AdminID, &i.AdminID,
&i.WalletID, &i.WalletID,
&i.DeductedPercentage, &i.DeductedPercentage,
@ -198,7 +218,7 @@ SET name = COALESCE($2, name),
), ),
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
WHERE id = $1 WHERE id = $1
RETURNING id, name, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at RETURNING id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at
` `
type UpdateCompanyParams struct { type UpdateCompanyParams struct {
@ -221,6 +241,7 @@ func (q *Queries) UpdateCompany(ctx context.Context, arg UpdateCompanyParams) (C
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Slug,
&i.AdminID, &i.AdminID,
&i.WalletID, &i.WalletID,
&i.DeductedPercentage, &i.DeductedPercentage,

View File

@ -7,48 +7,70 @@ package dbgen
import ( import (
"context" "context"
"github.com/jackc/pgx/v5/pgtype"
) )
const DeleteCustomOddsByID = `-- name: DeleteCustomOddsByID :exec const DeleteCustomOddByEventID = `-- name: DeleteCustomOddByEventID :exec
DELETE FROM disabled_odd DELETE FROM custom_odds_market
WHERE raw_odd_id = $1 WHERE event_id = $1
` `
func (q *Queries) DeleteCustomOddsByID(ctx context.Context, rawOddID int64) error { func (q *Queries) DeleteCustomOddByEventID(ctx context.Context, eventID string) error {
_, err := q.db.Exec(ctx, DeleteCustomOddsByID, rawOddID) _, err := q.db.Exec(ctx, DeleteCustomOddByEventID, eventID)
return err return err
} }
const DeleteCustomOddsByRawOddID = `-- name: DeleteCustomOddsByRawOddID :exec const DeleteCustomOddsByID = `-- name: DeleteCustomOddsByID :exec
DELETE FROM disabled_odd DELETE FROM custom_odds_market
WHERE raw_odd_id = $1 WHERE id = $1
` `
func (q *Queries) DeleteCustomOddsByRawOddID(ctx context.Context, rawOddID int64) error { func (q *Queries) DeleteCustomOddsByID(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteCustomOddsByRawOddID, rawOddID) _, err := q.db.Exec(ctx, DeleteCustomOddsByID, id)
return err
}
const DeleteCustomOddsByOddID = `-- name: DeleteCustomOddsByOddID :exec
DELETE FROM custom_odds_market
WHERE odds_market_id = $1
AND company_id = $2
`
type DeleteCustomOddsByOddIDParams struct {
OddsMarketID int64 `json:"odds_market_id"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) DeleteCustomOddsByOddID(ctx context.Context, arg DeleteCustomOddsByOddIDParams) error {
_, err := q.db.Exec(ctx, DeleteCustomOddsByOddID, arg.OddsMarketID, arg.CompanyID)
return err return err
} }
const GetAllCustomOdds = `-- name: GetAllCustomOdds :many const GetAllCustomOdds = `-- name: GetAllCustomOdds :many
SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at
FROM custom_odd FROM custom_odds_market
WHERE (
company_id = $1
OR $1 IS NULL
)
` `
func (q *Queries) GetAllCustomOdds(ctx context.Context) ([]CustomOdd, error) { func (q *Queries) GetAllCustomOdds(ctx context.Context, companyID pgtype.Int8) ([]CustomOddsMarket, error) {
rows, err := q.db.Query(ctx, GetAllCustomOdds) rows, err := q.db.Query(ctx, GetAllCustomOdds, companyID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []CustomOdd var items []CustomOddsMarket
for rows.Next() { for rows.Next() {
var i CustomOdd var i CustomOddsMarket
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.OddID, &i.CompanyID,
&i.RawOddID, &i.OddsMarketID,
&i.EventID, &i.EventID,
&i.OddValue, &i.RawOdds,
&i.CreatedAt, &i.CreatedAt,
); err != nil { ); err != nil {
return nil, err return nil, err
@ -62,77 +84,83 @@ func (q *Queries) GetAllCustomOdds(ctx context.Context) ([]CustomOdd, error) {
} }
const GetCustomOddByID = `-- name: GetCustomOddByID :one const GetCustomOddByID = `-- name: GetCustomOddByID :one
SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at
FROM custom_odd FROM custom_odds_market
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) GetCustomOddByID(ctx context.Context, id int64) (CustomOdd, error) { func (q *Queries) GetCustomOddByID(ctx context.Context, id int64) (CustomOddsMarket, error) {
row := q.db.QueryRow(ctx, GetCustomOddByID, id) row := q.db.QueryRow(ctx, GetCustomOddByID, id)
var i CustomOdd var i CustomOddsMarket
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.OddID, &i.CompanyID,
&i.RawOddID, &i.OddsMarketID,
&i.EventID, &i.EventID,
&i.OddValue, &i.RawOdds,
&i.CreatedAt, &i.CreatedAt,
) )
return i, err return i, err
} }
const GetCustomOddByRawOddID = `-- name: GetCustomOddByRawOddID :one const GetCustomOddByOddID = `-- name: GetCustomOddByOddID :one
SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at
FROM custom_odd FROM custom_odds_market
WHERE raw_odd_id = $1 WHERE odds_market_id = $1
AND company_id = $2
` `
func (q *Queries) GetCustomOddByRawOddID(ctx context.Context, rawOddID int64) (CustomOdd, error) { type GetCustomOddByOddIDParams struct {
row := q.db.QueryRow(ctx, GetCustomOddByRawOddID, rawOddID) OddsMarketID int64 `json:"odds_market_id"`
var i CustomOdd CompanyID int64 `json:"company_id"`
}
func (q *Queries) GetCustomOddByOddID(ctx context.Context, arg GetCustomOddByOddIDParams) (CustomOddsMarket, error) {
row := q.db.QueryRow(ctx, GetCustomOddByOddID, arg.OddsMarketID, arg.CompanyID)
var i CustomOddsMarket
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.OddID, &i.CompanyID,
&i.RawOddID, &i.OddsMarketID,
&i.EventID, &i.EventID,
&i.OddValue, &i.RawOdds,
&i.CreatedAt, &i.CreatedAt,
) )
return i, err return i, err
} }
const InsertCustomOdd = `-- name: InsertCustomOdd :one const InsertCustomOddsMarket = `-- name: InsertCustomOddsMarket :one
INSERT INTO custom_odd ( INSERT INTO custom_odds_market (
odd_id, odds_market_id,
raw_odd_id, company_id,
event_id, event_id,
odd_value raw_odds
) )
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4)
RETURNING id, odd_id, raw_odd_id, event_id, odd_value, created_at RETURNING id, company_id, odds_market_id, event_id, raw_odds, created_at
` `
type InsertCustomOddParams struct { type InsertCustomOddsMarketParams struct {
OddID int64 `json:"odd_id"` OddsMarketID int64 `json:"odds_market_id"`
RawOddID int64 `json:"raw_odd_id"` CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"` EventID string `json:"event_id"`
OddValue float64 `json:"odd_value"` RawOdds []byte `json:"raw_odds"`
} }
func (q *Queries) InsertCustomOdd(ctx context.Context, arg InsertCustomOddParams) (CustomOdd, error) { func (q *Queries) InsertCustomOddsMarket(ctx context.Context, arg InsertCustomOddsMarketParams) (CustomOddsMarket, error) {
row := q.db.QueryRow(ctx, InsertCustomOdd, row := q.db.QueryRow(ctx, InsertCustomOddsMarket,
arg.OddID, arg.OddsMarketID,
arg.RawOddID, arg.CompanyID,
arg.EventID, arg.EventID,
arg.OddValue, arg.RawOdds,
) )
var i CustomOdd var i CustomOddsMarket
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.OddID, &i.CompanyID,
&i.RawOddID, &i.OddsMarketID,
&i.EventID, &i.EventID,
&i.OddValue, &i.RawOdds,
&i.CreatedAt, &i.CreatedAt,
) )
return i, err return i, err

View File

@ -30,7 +30,7 @@ func (q *Queries) DeleteDisabledOddsByRawOddID(ctx context.Context, rawOddID int
} }
const GetAllDisabledOdds = `-- name: GetAllDisabledOdds :many const GetAllDisabledOdds = `-- name: GetAllDisabledOdds :many
SELECT id, odd_id, raw_odd_id, event_id, created_at SELECT id, company_id, odds_market_id, raw_odd_id, event_id, created_at
FROM disabled_odd FROM disabled_odd
` `
@ -45,7 +45,8 @@ func (q *Queries) GetAllDisabledOdds(ctx context.Context) ([]DisabledOdd, error)
var i DisabledOdd var i DisabledOdd
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.OddID, &i.CompanyID,
&i.OddsMarketID,
&i.RawOddID, &i.RawOddID,
&i.EventID, &i.EventID,
&i.CreatedAt, &i.CreatedAt,
@ -61,7 +62,7 @@ func (q *Queries) GetAllDisabledOdds(ctx context.Context) ([]DisabledOdd, error)
} }
const GetDisabledOddByID = `-- name: GetDisabledOddByID :one const GetDisabledOddByID = `-- name: GetDisabledOddByID :one
SELECT id, odd_id, raw_odd_id, event_id, created_at SELECT id, company_id, odds_market_id, raw_odd_id, event_id, created_at
FROM disabled_odd FROM disabled_odd
WHERE raw_odd_id = $1 WHERE raw_odd_id = $1
` `
@ -71,7 +72,8 @@ func (q *Queries) GetDisabledOddByID(ctx context.Context, rawOddID int64) (Disab
var i DisabledOdd var i DisabledOdd
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.OddID, &i.CompanyID,
&i.OddsMarketID,
&i.RawOddID, &i.RawOddID,
&i.EventID, &i.EventID,
&i.CreatedAt, &i.CreatedAt,
@ -80,7 +82,7 @@ func (q *Queries) GetDisabledOddByID(ctx context.Context, rawOddID int64) (Disab
} }
const GetDisabledOddByRawOddID = `-- name: GetDisabledOddByRawOddID :one const GetDisabledOddByRawOddID = `-- name: GetDisabledOddByRawOddID :one
SELECT id, odd_id, raw_odd_id, event_id, created_at SELECT id, company_id, odds_market_id, raw_odd_id, event_id, created_at
FROM disabled_odd FROM disabled_odd
WHERE raw_odd_id = $1 WHERE raw_odd_id = $1
` `
@ -90,7 +92,8 @@ func (q *Queries) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64)
var i DisabledOdd var i DisabledOdd
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.OddID, &i.CompanyID,
&i.OddsMarketID,
&i.RawOddID, &i.RawOddID,
&i.EventID, &i.EventID,
&i.CreatedAt, &i.CreatedAt,
@ -100,26 +103,34 @@ func (q *Queries) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64)
const InsertDisabledOdds = `-- name: InsertDisabledOdds :one const InsertDisabledOdds = `-- name: InsertDisabledOdds :one
INSERT INTO disabled_odd ( INSERT INTO disabled_odd (
odd_id, odds_market_id,
company_id,
event_id, event_id,
raw_odd_id raw_odd_id
) )
VALUES ($1, $2, $3) VALUES ($1, $2, $3, $4)
RETURNING id, odd_id, raw_odd_id, event_id, created_at RETURNING id, company_id, odds_market_id, raw_odd_id, event_id, created_at
` `
type InsertDisabledOddsParams struct { type InsertDisabledOddsParams struct {
OddID int64 `json:"odd_id"` OddsMarketID int64 `json:"odds_market_id"`
CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"` EventID string `json:"event_id"`
RawOddID int64 `json:"raw_odd_id"` RawOddID int64 `json:"raw_odd_id"`
} }
func (q *Queries) InsertDisabledOdds(ctx context.Context, arg InsertDisabledOddsParams) (DisabledOdd, error) { func (q *Queries) InsertDisabledOdds(ctx context.Context, arg InsertDisabledOddsParams) (DisabledOdd, error) {
row := q.db.QueryRow(ctx, InsertDisabledOdds, arg.OddID, arg.EventID, arg.RawOddID) row := q.db.QueryRow(ctx, InsertDisabledOdds,
arg.OddsMarketID,
arg.CompanyID,
arg.EventID,
arg.RawOddID,
)
var i DisabledOdd var i DisabledOdd
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.OddID, &i.CompanyID,
&i.OddsMarketID,
&i.RawOddID, &i.RawOddID,
&i.EventID, &i.EventID,
&i.CreatedAt, &i.CreatedAt,

File diff suppressed because it is too large Load Diff

View File

@ -56,10 +56,6 @@ SELECT leagues.id,
) AS removed ) AS removed
FROM leagues FROM leagues
JOIN events ON leagues.id = events.league_id JOIN events ON leagues.id = events.league_id
WHERE (
leagues.is_featured = $1
OR $1 IS NULL
)
GROUP BY leagues.id, GROUP BY leagues.id,
leagues.name leagues.name
` `
@ -83,8 +79,8 @@ type GetLeagueEventStatRow struct {
Removed int64 `json:"removed"` Removed int64 `json:"removed"`
} }
func (q *Queries) GetLeagueEventStat(ctx context.Context, isLeagueFeatured pgtype.Bool) ([]GetLeagueEventStatRow, error) { func (q *Queries) GetLeagueEventStat(ctx context.Context) ([]GetLeagueEventStatRow, error) {
rows, err := q.db.Query(ctx, GetLeagueEventStat, isLeagueFeatured) rows, err := q.db.Query(ctx, GetLeagueEventStat)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -126,34 +122,20 @@ SELECT DATE_TRUNC('month', start_time) AS month,
FROM events FROM events
JOIN leagues ON leagues.id = events.league_id JOIN leagues ON leagues.id = events.league_id
WHERE ( WHERE (
events.is_featured = $1 events.league_id = $1
OR $1 IS NULL OR $1 IS NULL
) )
AND (
leagues.is_featured = $2
OR $2 IS NULL
)
AND (
events.league_id = $3
OR $3 IS NULL
)
GROUP BY month GROUP BY month
ORDER BY month ORDER BY month
` `
type GetTotalMontlyEventStatParams struct {
IsEventFeatured pgtype.Bool `json:"is_event_featured"`
IsLeagueFeatured pgtype.Bool `json:"is_league_featured"`
LeagueID pgtype.Int4 `json:"league_id"`
}
type GetTotalMontlyEventStatRow struct { type GetTotalMontlyEventStatRow struct {
Month pgtype.Interval `json:"month"` Month pgtype.Interval `json:"month"`
EventCount int64 `json:"event_count"` EventCount int64 `json:"event_count"`
} }
func (q *Queries) GetTotalMontlyEventStat(ctx context.Context, arg GetTotalMontlyEventStatParams) ([]GetTotalMontlyEventStatRow, error) { func (q *Queries) GetTotalMontlyEventStat(ctx context.Context, leagueID pgtype.Int8) ([]GetTotalMontlyEventStatRow, error) {
rows, err := q.db.Query(ctx, GetTotalMontlyEventStat, arg.IsEventFeatured, arg.IsLeagueFeatured, arg.LeagueID) rows, err := q.db.Query(ctx, GetTotalMontlyEventStat, leagueID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -14,26 +14,26 @@ import (
const CreateFlag = `-- name: CreateFlag :one const CreateFlag = `-- name: CreateFlag :one
INSERT INTO flags ( INSERT INTO flags (
bet_id, bet_id,
odd_id, odds_market_id,
reason reason
) VALUES ( ) VALUES (
$1, $2, $3 $1, $2, $3
) RETURNING id, bet_id, odd_id, reason, flagged_at, resolved ) RETURNING id, bet_id, odds_market_id, reason, flagged_at, resolved
` `
type CreateFlagParams struct { type CreateFlagParams struct {
BetID pgtype.Int8 `json:"bet_id"` BetID pgtype.Int8 `json:"bet_id"`
OddID pgtype.Int8 `json:"odd_id"` OddsMarketID pgtype.Int8 `json:"odds_market_id"`
Reason pgtype.Text `json:"reason"` Reason pgtype.Text `json:"reason"`
} }
func (q *Queries) CreateFlag(ctx context.Context, arg CreateFlagParams) (Flag, error) { func (q *Queries) CreateFlag(ctx context.Context, arg CreateFlagParams) (Flag, error) {
row := q.db.QueryRow(ctx, CreateFlag, arg.BetID, arg.OddID, arg.Reason) row := q.db.QueryRow(ctx, CreateFlag, arg.BetID, arg.OddsMarketID, arg.Reason)
var i Flag var i Flag
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.BetID, &i.BetID,
&i.OddID, &i.OddsMarketID,
&i.Reason, &i.Reason,
&i.FlaggedAt, &i.FlaggedAt,
&i.Resolved, &i.Resolved,

View File

@ -14,27 +14,27 @@ import (
const CheckLeagueSupport = `-- name: CheckLeagueSupport :one const CheckLeagueSupport = `-- name: CheckLeagueSupport :one
SELECT EXISTS( SELECT EXISTS(
SELECT 1 SELECT 1
FROM leagues FROM company_league_settings
WHERE id = $1 WHERE league_id = $1
AND company_id = $2
AND is_active = true AND is_active = true
) )
` `
func (q *Queries) CheckLeagueSupport(ctx context.Context, id int64) (bool, error) { type CheckLeagueSupportParams struct {
row := q.db.QueryRow(ctx, CheckLeagueSupport, id) LeagueID int64 `json:"league_id"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) CheckLeagueSupport(ctx context.Context, arg CheckLeagueSupportParams) (bool, error) {
row := q.db.QueryRow(ctx, CheckLeagueSupport, arg.LeagueID, arg.CompanyID)
var exists bool var exists bool
err := row.Scan(&exists) err := row.Scan(&exists)
return exists, err return exists, err
} }
const GetAllLeagues = `-- name: GetAllLeagues :many const GetAllLeagues = `-- name: GetAllLeagues :many
SELECT id, SELECT id, name, img_url, country_code, bet365_id, sport_id, default_is_active, default_is_featured
name,
country_code,
bet365_id,
is_active,
is_featured,
sport_id
FROM leagues FROM leagues
WHERE ( WHERE (
country_code = $1 country_code = $1
@ -44,44 +44,21 @@ WHERE (
sport_id = $2 sport_id = $2
OR $2 IS NULL OR $2 IS NULL
) )
AND ( ORDER BY name ASC
is_active = $3 LIMIT $4 OFFSET $3
OR $3 IS NULL
)
AND (
is_featured = $4
OR $4 IS NULL
)
ORDER BY is_featured DESC,
name ASC
LIMIT $6 OFFSET $5
` `
type GetAllLeaguesParams struct { type GetAllLeaguesParams struct {
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
SportID pgtype.Int4 `json:"sport_id"` SportID pgtype.Int4 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
Offset pgtype.Int4 `json:"offset"` Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"` Limit pgtype.Int4 `json:"limit"`
} }
type GetAllLeaguesRow struct { func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([]League, error) {
ID int64 `json:"id"`
Name string `json:"name"`
CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
SportID int32 `json:"sport_id"`
}
func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([]GetAllLeaguesRow, error) {
rows, err := q.db.Query(ctx, GetAllLeagues, rows, err := q.db.Query(ctx, GetAllLeagues,
arg.CountryCode, arg.CountryCode,
arg.SportID, arg.SportID,
arg.IsActive,
arg.IsFeatured,
arg.Offset, arg.Offset,
arg.Limit, arg.Limit,
) )
@ -89,17 +66,18 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []GetAllLeaguesRow var items []League
for rows.Next() { for rows.Next() {
var i GetAllLeaguesRow var i League
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.ImgUrl,
&i.CountryCode, &i.CountryCode,
&i.Bet365ID, &i.Bet365ID,
&i.IsActive,
&i.IsFeatured,
&i.SportID, &i.SportID,
&i.DefaultIsActive,
&i.DefaultIsFeatured,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -111,45 +89,71 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([
return items, nil return items, nil
} }
const GetFeaturedLeagues = `-- name: GetFeaturedLeagues :many const GetAllLeaguesWithSettings = `-- name: GetAllLeaguesWithSettings :many
SELECT id, SELECT id, name, img_url, country_code, bet365_id, sport_id, default_is_active, default_is_featured, company_id, is_active, is_featured, updated_at
name, FROM league_with_settings
country_code, WHERE (company_id = $1)
bet365_id, AND (
is_active, country_code = $2
is_featured, OR $2 IS NULL
sport_id )
FROM leagues AND (
WHERE is_featured = true sport_id = $3
OR $3 IS NULL
)
AND (
is_active = $4
OR $4 IS NULL
)
AND (
is_featured = $5
OR $5 IS NULL
)
ORDER BY is_featured DESC,
name ASC
LIMIT $7 OFFSET $6
` `
type GetFeaturedLeaguesRow struct { type GetAllLeaguesWithSettingsParams struct {
ID int64 `json:"id"` CompanyID int64 `json:"company_id"`
Name string `json:"name"`
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"` SportID pgtype.Int4 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"` IsFeatured pgtype.Bool `json:"is_featured"`
SportID int32 `json:"sport_id"` Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
} }
func (q *Queries) GetFeaturedLeagues(ctx context.Context) ([]GetFeaturedLeaguesRow, error) { func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeaguesWithSettingsParams) ([]LeagueWithSetting, error) {
rows, err := q.db.Query(ctx, GetFeaturedLeagues) rows, err := q.db.Query(ctx, GetAllLeaguesWithSettings,
arg.CompanyID,
arg.CountryCode,
arg.SportID,
arg.IsActive,
arg.IsFeatured,
arg.Offset,
arg.Limit,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []GetFeaturedLeaguesRow var items []LeagueWithSetting
for rows.Next() { for rows.Next() {
var i GetFeaturedLeaguesRow var i LeagueWithSetting
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.ImgUrl,
&i.CountryCode, &i.CountryCode,
&i.Bet365ID, &i.Bet365ID,
&i.SportID,
&i.DefaultIsActive,
&i.DefaultIsFeatured,
&i.CompanyID,
&i.IsActive, &i.IsActive,
&i.IsFeatured, &i.IsFeatured,
&i.SportID, &i.UpdatedAt,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -168,16 +172,14 @@ INSERT INTO leagues (
country_code, country_code,
bet365_id, bet365_id,
sport_id, sport_id,
is_active, default_is_active,
is_featured default_is_featured
) )
VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO
UPDATE UPDATE
SET name = EXCLUDED.name, SET name = EXCLUDED.name,
country_code = EXCLUDED.country_code, country_code = EXCLUDED.country_code,
bet365_id = EXCLUDED.bet365_id, bet365_id = EXCLUDED.bet365_id,
is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured,
sport_id = EXCLUDED.sport_id sport_id = EXCLUDED.sport_id
` `
@ -187,8 +189,8 @@ type InsertLeagueParams struct {
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"` Bet365ID pgtype.Int4 `json:"bet365_id"`
SportID int32 `json:"sport_id"` SportID int32 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"` DefaultIsActive bool `json:"default_is_active"`
IsFeatured pgtype.Bool `json:"is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
} }
func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) error { func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) error {
@ -198,25 +200,39 @@ func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) erro
arg.CountryCode, arg.CountryCode,
arg.Bet365ID, arg.Bet365ID,
arg.SportID, arg.SportID,
arg.IsActive, arg.DefaultIsActive,
arg.IsFeatured, arg.DefaultIsFeatured,
) )
return err return err
} }
const SetLeagueActive = `-- name: SetLeagueActive :exec const InsertLeagueSettings = `-- name: InsertLeagueSettings :exec
UPDATE leagues INSERT INTO company_league_settings (
SET is_active = $2 company_id,
WHERE id = $1 league_id,
is_active,
is_featured
)
VALUES ($1, $2, $3, $4) ON CONFLICT(company_id, league_id) DO
UPDATE
SET is_active = EXCLUDED.is_active,
is_featured = EXCLUDED.is_featured
` `
type SetLeagueActiveParams struct { type InsertLeagueSettingsParams struct {
ID int64 `json:"id"` CompanyID int64 `json:"company_id"`
LeagueID int64 `json:"league_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
} }
func (q *Queries) SetLeagueActive(ctx context.Context, arg SetLeagueActiveParams) error { func (q *Queries) InsertLeagueSettings(ctx context.Context, arg InsertLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, SetLeagueActive, arg.ID, arg.IsActive) _, err := q.db.Exec(ctx, InsertLeagueSettings,
arg.CompanyID,
arg.LeagueID,
arg.IsActive,
arg.IsFeatured,
)
return err return err
} }
@ -225,9 +241,7 @@ UPDATE leagues
SET name = COALESCE($2, name), SET name = COALESCE($2, name),
country_code = COALESCE($3, country_code), country_code = COALESCE($3, country_code),
bet365_id = COALESCE($4, bet365_id), bet365_id = COALESCE($4, bet365_id),
is_active = COALESCE($5, is_active), sport_id = COALESCE($5, sport_id)
is_featured = COALESCE($6, is_featured),
sport_id = COALESCE($7, sport_id)
WHERE id = $1 WHERE id = $1
` `
@ -236,8 +250,6 @@ type UpdateLeagueParams struct {
Name pgtype.Text `json:"name"` Name pgtype.Text `json:"name"`
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"` Bet365ID pgtype.Int4 `json:"bet365_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
SportID pgtype.Int4 `json:"sport_id"` SportID pgtype.Int4 `json:"sport_id"`
} }
@ -247,43 +259,35 @@ func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) erro
arg.Name, arg.Name,
arg.CountryCode, arg.CountryCode,
arg.Bet365ID, arg.Bet365ID,
arg.IsActive,
arg.IsFeatured,
arg.SportID, arg.SportID,
) )
return err return err
} }
const UpdateLeagueByBet365ID = `-- name: UpdateLeagueByBet365ID :exec const UpdateLeagueSettings = `-- name: UpdateLeagueSettings :exec
UPDATE leagues UPDATE company_league_settings
SET name = COALESCE($2, name), SET is_active = COALESCE($3, is_active),
id = COALESCE($3, id), is_featured = COALESCE(
country_code = COALESCE($4, country_code), $4,
is_active = COALESCE($5, is_active), is_featured
is_featured = COALESCE($6, is_featured), )
sport_id = COALESCE($7, sport_id) WHERE league_id = $1
WHERE bet365_id = $1 AND company_id = $2
` `
type UpdateLeagueByBet365IDParams struct { type UpdateLeagueSettingsParams struct {
Bet365ID pgtype.Int4 `json:"bet365_id"` LeagueID int64 `json:"league_id"`
Name pgtype.Text `json:"name"` CompanyID int64 `json:"company_id"`
ID pgtype.Int8 `json:"id"`
CountryCode pgtype.Text `json:"country_code"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"` IsFeatured pgtype.Bool `json:"is_featured"`
SportID pgtype.Int4 `json:"sport_id"`
} }
func (q *Queries) UpdateLeagueByBet365ID(ctx context.Context, arg UpdateLeagueByBet365IDParams) error { func (q *Queries) UpdateLeagueSettings(ctx context.Context, arg UpdateLeagueSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateLeagueByBet365ID, _, err := q.db.Exec(ctx, UpdateLeagueSettings,
arg.Bet365ID, arg.LeagueID,
arg.Name, arg.CompanyID,
arg.ID,
arg.CountryCode,
arg.IsActive, arg.IsActive,
arg.IsFeatured, arg.IsFeatured,
arg.SportID,
) )
return err return err
} }

View File

@ -75,6 +75,7 @@ type Bank struct {
type Bet struct { type Bet struct {
ID int64 `json:"id"` ID int64 `json:"id"`
CompanyID int64 `json:"company_id"`
Amount int64 `json:"amount"` Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"` TotalOdds float32 `json:"total_odds"`
Status int32 `json:"status"` Status int32 `json:"status"`
@ -108,6 +109,7 @@ type BetOutcome struct {
type BetWithOutcome struct { type BetWithOutcome struct {
ID int64 `json:"id"` ID int64 `json:"id"`
CompanyID int64 `json:"company_id"`
Amount int64 `json:"amount"` Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"` TotalOdds float32 `json:"total_odds"`
Status int32 `json:"status"` Status int32 `json:"status"`
@ -125,8 +127,8 @@ type BetWithOutcome struct {
} }
type Bonu struct { type Bonu struct {
ID int64 `json:"id"`
Multiplier float32 `json:"multiplier"` Multiplier float32 `json:"multiplier"`
ID int64 `json:"id"`
BalanceCap int64 `json:"balance_cap"` BalanceCap int64 `json:"balance_cap"`
} }
@ -184,6 +186,7 @@ type BranchOperation struct {
type CompaniesDetail struct { type CompaniesDetail struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Slug string `json:"slug"`
AdminID int64 `json:"admin_id"` AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"` WalletID int64 `json:"wallet_id"`
DeductedPercentage float32 `json:"deducted_percentage"` DeductedPercentage float32 `json:"deducted_percentage"`
@ -200,6 +203,7 @@ type CompaniesDetail struct {
type Company struct { type Company struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Slug string `json:"slug"`
AdminID int64 `json:"admin_id"` AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"` WalletID int64 `json:"wallet_id"`
DeductedPercentage float32 `json:"deducted_percentage"` DeductedPercentage float32 `json:"deducted_percentage"`
@ -208,13 +212,32 @@ type Company struct {
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
type CustomOdd struct { type CompanyEventSetting struct {
ID int64 `json:"id"` ID int64 `json:"id"`
OddID int64 `json:"odd_id"` CompanyID int64 `json:"company_id"`
RawOddID int64 `json:"raw_odd_id"`
EventID string `json:"event_id"` EventID string `json:"event_id"`
OddValue float64 `json:"odd_value"` IsActive pgtype.Bool `json:"is_active"`
CreatedAt pgtype.Timestamp `json:"created_at"` IsFeatured pgtype.Bool `json:"is_featured"`
WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type CompanyLeagueSetting struct {
ID int64 `json:"id"`
CompanyID int64 `json:"company_id"`
LeagueID int64 `json:"league_id"`
IsActive pgtype.Bool `json:"is_active"`
IsFeatured pgtype.Bool `json:"is_featured"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type CompanyOddSetting struct {
ID int64 `json:"id"`
CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
IsActive pgtype.Bool `json:"is_active"`
CustomRawOdds []byte `json:"custom_raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
type CustomerWallet struct { type CustomerWallet struct {
@ -259,7 +282,8 @@ type DirectDeposit struct {
type DisabledOdd struct { type DisabledOdd struct {
ID int64 `json:"id"` ID int64 `json:"id"`
OddID int64 `json:"odd_id"` CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
RawOddID int64 `json:"raw_odd_id"` RawOddID int64 `json:"raw_odd_id"`
EventID string `json:"event_id"` EventID string `json:"event_id"`
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
@ -267,31 +291,30 @@ type DisabledOdd struct {
type Event struct { type Event struct {
ID string `json:"id"` ID string `json:"id"`
SportID pgtype.Int4 `json:"sport_id"` SportID int32 `json:"sport_id"`
MatchName pgtype.Text `json:"match_name"` MatchName string `json:"match_name"`
HomeTeam pgtype.Text `json:"home_team"` HomeTeam string `json:"home_team"`
AwayTeam pgtype.Text `json:"away_team"` AwayTeam string `json:"away_team"`
HomeTeamID pgtype.Int4 `json:"home_team_id"` HomeTeamID int64 `json:"home_team_id"`
AwayTeamID pgtype.Int4 `json:"away_team_id"` AwayTeamID int64 `json:"away_team_id"`
HomeKitImage pgtype.Text `json:"home_kit_image"` HomeKitImage string `json:"home_kit_image"`
AwayKitImage pgtype.Text `json:"away_kit_image"` AwayKitImage string `json:"away_kit_image"`
LeagueID pgtype.Int4 `json:"league_id"` LeagueID int64 `json:"league_id"`
LeagueName pgtype.Text `json:"league_name"` LeagueName string `json:"league_name"`
LeagueCc pgtype.Text `json:"league_cc"`
StartTime pgtype.Timestamp `json:"start_time"` StartTime pgtype.Timestamp `json:"start_time"`
Score pgtype.Text `json:"score"` Score pgtype.Text `json:"score"`
MatchMinute pgtype.Int4 `json:"match_minute"` MatchMinute pgtype.Int4 `json:"match_minute"`
TimerStatus pgtype.Text `json:"timer_status"` TimerStatus pgtype.Text `json:"timer_status"`
AddedTime pgtype.Int4 `json:"added_time"` AddedTime pgtype.Int4 `json:"added_time"`
MatchPeriod pgtype.Int4 `json:"match_period"` MatchPeriod pgtype.Int4 `json:"match_period"`
IsLive pgtype.Bool `json:"is_live"` IsLive bool `json:"is_live"`
Status pgtype.Text `json:"status"` Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
Source pgtype.Text `json:"source"` Source string `json:"source"`
IsFeatured bool `json:"is_featured"` DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"` IsMonitored bool `json:"is_monitored"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
IsActive bool `json:"is_active"`
} }
type EventHistory struct { type EventHistory struct {
@ -301,6 +324,69 @@ type EventHistory struct {
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
} }
type EventWithCountry struct {
ID string `json:"id"`
SportID int32 `json:"sport_id"`
MatchName string `json:"match_name"`
HomeTeam string `json:"home_team"`
AwayTeam string `json:"away_team"`
HomeTeamID int64 `json:"home_team_id"`
AwayTeamID int64 `json:"away_team_id"`
HomeKitImage string `json:"home_kit_image"`
AwayKitImage string `json:"away_kit_image"`
LeagueID int64 `json:"league_id"`
LeagueName string `json:"league_name"`
StartTime pgtype.Timestamp `json:"start_time"`
Score pgtype.Text `json:"score"`
MatchMinute pgtype.Int4 `json:"match_minute"`
TimerStatus pgtype.Text `json:"timer_status"`
AddedTime pgtype.Int4 `json:"added_time"`
MatchPeriod pgtype.Int4 `json:"match_period"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"`
LeagueCc pgtype.Text `json:"league_cc"`
}
type EventWithSetting struct {
ID string `json:"id"`
SportID int32 `json:"sport_id"`
MatchName string `json:"match_name"`
HomeTeam string `json:"home_team"`
AwayTeam string `json:"away_team"`
HomeTeamID int64 `json:"home_team_id"`
AwayTeamID int64 `json:"away_team_id"`
HomeKitImage string `json:"home_kit_image"`
AwayKitImage string `json:"away_kit_image"`
LeagueID int64 `json:"league_id"`
LeagueName string `json:"league_name"`
StartTime pgtype.Timestamp `json:"start_time"`
Score pgtype.Text `json:"score"`
MatchMinute pgtype.Int4 `json:"match_minute"`
TimerStatus pgtype.Text `json:"timer_status"`
AddedTime pgtype.Int4 `json:"added_time"`
MatchPeriod pgtype.Int4 `json:"match_period"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
Source string `json:"source"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
IsMonitored bool `json:"is_monitored"`
CompanyID int64 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
LeagueCc pgtype.Text `json:"league_cc"`
}
type ExchangeRate struct { type ExchangeRate struct {
ID int32 `json:"id"` ID int32 `json:"id"`
FromCurrency string `json:"from_currency"` FromCurrency string `json:"from_currency"`
@ -320,7 +406,7 @@ type FavoriteGame struct {
type Flag struct { type Flag struct {
ID int64 `json:"id"` ID int64 `json:"id"`
BetID pgtype.Int8 `json:"bet_id"` BetID pgtype.Int8 `json:"bet_id"`
OddID pgtype.Int8 `json:"odd_id"` OddsMarketID pgtype.Int8 `json:"odds_market_id"`
Reason pgtype.Text `json:"reason"` Reason pgtype.Text `json:"reason"`
FlaggedAt pgtype.Timestamp `json:"flagged_at"` FlaggedAt pgtype.Timestamp `json:"flagged_at"`
Resolved pgtype.Bool `json:"resolved"` Resolved pgtype.Bool `json:"resolved"`
@ -329,12 +415,27 @@ type Flag struct {
type League struct { type League struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Img pgtype.Text `json:"img"` ImgUrl pgtype.Text `json:"img_url"`
CountryCode pgtype.Text `json:"country_code"` CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"` Bet365ID pgtype.Int4 `json:"bet365_id"`
SportID int32 `json:"sport_id"` SportID int32 `json:"sport_id"`
IsActive pgtype.Bool `json:"is_active"` DefaultIsActive bool `json:"default_is_active"`
IsFeatured pgtype.Bool `json:"is_featured"` DefaultIsFeatured bool `json:"default_is_featured"`
}
type LeagueWithSetting struct {
ID int64 `json:"id"`
Name string `json:"name"`
ImgUrl pgtype.Text `json:"img_url"`
CountryCode pgtype.Text `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"`
SportID int32 `json:"sport_id"`
DefaultIsActive bool `json:"default_is_active"`
DefaultIsFeatured bool `json:"default_is_featured"`
CompanyID int64 `json:"company_id"`
IsActive bool `json:"is_active"`
IsFeatured bool `json:"is_featured"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
type Notification struct { type Notification struct {
@ -354,29 +455,9 @@ type Notification struct {
Metadata []byte `json:"metadata"` Metadata []byte `json:"metadata"`
} }
type Odd struct {
ID int64 `json:"id"`
EventID pgtype.Text `json:"event_id"`
Fi pgtype.Text `json:"fi"`
MarketType string `json:"market_type"`
MarketName pgtype.Text `json:"market_name"`
MarketCategory pgtype.Text `json:"market_category"`
MarketID pgtype.Text `json:"market_id"`
Name pgtype.Text `json:"name"`
Handicap pgtype.Text `json:"handicap"`
OddsValue pgtype.Float8 `json:"odds_value"`
Section string `json:"section"`
Category pgtype.Text `json:"category"`
RawOdds []byte `json:"raw_odds"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
Source pgtype.Text `json:"source"`
IsActive pgtype.Bool `json:"is_active"`
}
type OddHistory struct { type OddHistory struct {
ID int64 `json:"id"` ID int64 `json:"id"`
OddID int64 `json:"odd_id"` OddsMarketID int64 `json:"odds_market_id"`
RawOddID int64 `json:"raw_odd_id"` RawOddID int64 `json:"raw_odd_id"`
MarketID string `json:"market_id"` MarketID string `json:"market_id"`
EventID string `json:"event_id"` EventID string `json:"event_id"`
@ -384,6 +465,53 @@ type OddHistory struct {
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
} }
type OddsMarket struct {
ID int64 `json:"id"`
EventID string `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"`
RawOdds []byte `json:"raw_odds"`
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 string `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `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"`
}
type OddsMarketWithSetting struct {
ID int64 `json:"id"`
EventID string `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"`
RawOdds []byte `json:"raw_odds"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
CompanyID int64 `json:"company_id"`
IsActive bool `json:"is_active"`
RawOdds []byte `json:"raw_odds"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type Otp struct { type Otp struct {
ID int64 `json:"id"` ID int64 `json:"id"`
SentTo string `json:"sent_to"` SentTo string `json:"sent_to"`
@ -599,15 +727,16 @@ type SupportedOperation struct {
} }
type Team struct { type Team struct {
ID string `json:"id"` ID int64 `json:"id"`
TeamName string `json:"team_name"` TeamName string `json:"team_name"`
Country pgtype.Text `json:"country"` CountryCode string `json:"country_code"`
Bet365ID pgtype.Int4 `json:"bet365_id"` Bet365ID pgtype.Int8 `json:"bet365_id"`
LogoUrl pgtype.Text `json:"logo_url"` ImgUrl pgtype.Text `json:"img_url"`
} }
type Ticket struct { type Ticket struct {
ID int64 `json:"id"` ID int64 `json:"id"`
CompanyID int64 `json:"company_id"`
Amount int64 `json:"amount"` Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"` TotalOdds float32 `json:"total_odds"`
Ip string `json:"ip"` Ip string `json:"ip"`
@ -634,6 +763,7 @@ type TicketOutcome struct {
type TicketWithOutcome struct { type TicketWithOutcome struct {
ID int64 `json:"id"` ID int64 `json:"id"`
CompanyID int64 `json:"company_id"`
Amount int64 `json:"amount"` Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"` TotalOdds float32 `json:"total_odds"`
Ip string `json:"ip"` Ip string `json:"ip"`
@ -738,6 +868,7 @@ type VirtualGameTransaction struct {
type Wallet struct { type Wallet struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Balance int64 `json:"balance"` Balance int64 `json:"balance"`
Currency string `json:"currency"`
IsWithdraw bool `json:"is_withdraw"` IsWithdraw bool `json:"is_withdraw"`
IsBettable bool `json:"is_bettable"` IsBettable bool `json:"is_bettable"`
IsTransferable bool `json:"is_transferable"` IsTransferable bool `json:"is_transferable"`
@ -746,7 +877,6 @@ type Wallet struct {
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"`
Currency string `json:"currency"`
BonusBalance pgtype.Numeric `json:"bonus_balance"` BonusBalance pgtype.Numeric `json:"bonus_balance"`
CashBalance pgtype.Numeric `json:"cash_balance"` CashBalance pgtype.Numeric `json:"cash_balance"`
} }

View File

@ -12,10 +12,10 @@ import (
) )
const GetAllOddHistory = `-- name: GetAllOddHistory :many const GetAllOddHistory = `-- name: GetAllOddHistory :many
SELECT id, odd_id, raw_odd_id, market_id, event_id, odd_value, created_at SELECT id, odds_market_id, raw_odd_id, market_id, event_id, odd_value, created_at
FROM odd_history FROM odd_history
WHERE ( WHERE (
odd_id = $1 odds_market_id = $1
OR $1 IS NULL OR $1 IS NULL
) )
AND ( AND (
@ -67,7 +67,7 @@ func (q *Queries) GetAllOddHistory(ctx context.Context, arg GetAllOddHistoryPara
var i OddHistory var i OddHistory
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.OddID, &i.OddsMarketID,
&i.RawOddID, &i.RawOddID,
&i.MarketID, &i.MarketID,
&i.EventID, &i.EventID,
@ -85,10 +85,10 @@ func (q *Queries) GetAllOddHistory(ctx context.Context, arg GetAllOddHistoryPara
} }
const GetInitialOddPerDay = `-- name: GetInitialOddPerDay :many const GetInitialOddPerDay = `-- name: GetInitialOddPerDay :many
SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) id, odd_id, raw_odd_id, market_id, event_id, odd_value, created_at SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) id, odds_market_id, raw_odd_id, market_id, event_id, odd_value, created_at
FROM odd_history FROM odd_history
WHERE ( WHERE (
odd_id = $2 odds_market_id = $2
OR $2 IS NULL OR $2 IS NULL
) )
AND ( AND (
@ -144,7 +144,7 @@ func (q *Queries) GetInitialOddPerDay(ctx context.Context, arg GetInitialOddPerD
var i OddHistory var i OddHistory
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.OddID, &i.OddsMarketID,
&i.RawOddID, &i.RawOddID,
&i.MarketID, &i.MarketID,
&i.EventID, &i.EventID,
@ -163,18 +163,18 @@ func (q *Queries) GetInitialOddPerDay(ctx context.Context, arg GetInitialOddPerD
const InsertOddHistory = `-- name: InsertOddHistory :one const InsertOddHistory = `-- name: InsertOddHistory :one
INSERT INTO odd_history ( INSERT INTO odd_history (
odd_id, odds_market_id,
market_id, market_id,
raw_odd_id, raw_odd_id,
event_id, event_id,
odd_value odd_value
) )
VALUES ($1, $2, $3, $4, $5) VALUES ($1, $2, $3, $4, $5)
RETURNING id, odd_id, raw_odd_id, market_id, event_id, odd_value, created_at RETURNING id, odds_market_id, raw_odd_id, market_id, event_id, odd_value, created_at
` `
type InsertOddHistoryParams struct { type InsertOddHistoryParams struct {
OddID int64 `json:"odd_id"` OddsMarketID int64 `json:"odds_market_id"`
MarketID string `json:"market_id"` MarketID string `json:"market_id"`
RawOddID int64 `json:"raw_odd_id"` RawOddID int64 `json:"raw_odd_id"`
EventID string `json:"event_id"` EventID string `json:"event_id"`
@ -183,7 +183,7 @@ type InsertOddHistoryParams struct {
func (q *Queries) InsertOddHistory(ctx context.Context, arg InsertOddHistoryParams) (OddHistory, error) { func (q *Queries) InsertOddHistory(ctx context.Context, arg InsertOddHistoryParams) (OddHistory, error) {
row := q.db.QueryRow(ctx, InsertOddHistory, row := q.db.QueryRow(ctx, InsertOddHistory,
arg.OddID, arg.OddsMarketID,
arg.MarketID, arg.MarketID,
arg.RawOddID, arg.RawOddID,
arg.EventID, arg.EventID,
@ -192,7 +192,7 @@ func (q *Queries) InsertOddHistory(ctx context.Context, arg InsertOddHistoryPara
var i OddHistory var i OddHistory
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.OddID, &i.OddsMarketID,
&i.RawOddID, &i.RawOddID,
&i.MarketID, &i.MarketID,
&i.EventID, &i.EventID,

View File

@ -12,49 +12,168 @@ import (
) )
const DeleteOddsForEvent = `-- name: DeleteOddsForEvent :exec const DeleteOddsForEvent = `-- name: DeleteOddsForEvent :exec
DELETE FROM odds DELETE FROM odds_market
Where fi = $1 Where event_id = $1
` `
func (q *Queries) DeleteOddsForEvent(ctx context.Context, fi pgtype.Text) error { func (q *Queries) DeleteOddsForEvent(ctx context.Context, eventID string) error {
_, err := q.db.Exec(ctx, DeleteOddsForEvent, fi) _, err := q.db.Exec(ctx, DeleteOddsForEvent, eventID)
return err return err
} }
const GetALLPrematchOdds = `-- name: GetALLPrematchOdds :many const GetAllOdds = `-- name: GetAllOdds :many
SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, expires_at, source, is_active SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds FROM odds_market_with_event
WHERE is_active = true LIMIT $2 OFFSET $1
AND source = 'bet365'
` `
func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]Odd, error) { type GetAllOddsParams struct {
rows, err := q.db.Query(ctx, GetALLPrematchOdds) Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
func (q *Queries) GetAllOdds(ctx context.Context, arg GetAllOddsParams) ([]OddsMarketWithEvent, error) {
rows, err := q.db.Query(ctx, GetAllOdds, arg.Offset, arg.Limit)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []Odd var items []OddsMarketWithEvent
for rows.Next() { for rows.Next() {
var i Odd var i OddsMarketWithEvent
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.EventID, &i.EventID,
&i.Fi,
&i.MarketType, &i.MarketType,
&i.MarketName, &i.MarketName,
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.Name,
&i.Handicap,
&i.OddsValue,
&i.Section,
&i.Category,
&i.RawOdds, &i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
&i.IsMonitored,
&i.IsLive,
&i.Status,
&i.Source, &i.Source,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetAllOddsWithSettings = `-- name: GetAllOddsWithSettings :many
SELECT id, event_id, market_type, market_name, market_category, market_id, odds_market_with_settings.raw_odds, default_is_active, fetched_at, expires_at, company_id, is_active, odds_market_with_settings.raw_odds, updated_at
FROM odds_market_with_settings
WHERE company_id = $1
LIMIT $3 OFFSET $2
`
type GetAllOddsWithSettingsParams struct {
CompanyID int64 `json:"company_id"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWithSettingsParams) ([]OddsMarketWithSetting, error) {
rows, err := q.db.Query(ctx, GetAllOddsWithSettings, arg.CompanyID, arg.Offset, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OddsMarketWithSetting
for rows.Next() {
var i OddsMarketWithSetting
if err := rows.Scan(
&i.ID,
&i.EventID,
&i.MarketType,
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
&i.CompanyID,
&i.IsActive, &i.IsActive,
&i.RawOdds,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetOddsByEventID = `-- name: GetOddsByEventID :many
SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds_market_with_event
WHERE event_id = $1
AND (
is_live = $2
OR $2 IS NULL
)
AND (
status = $3
OR $3 IS NULL
)
AND (
source = $4
OR $4 IS NULL
)
LIMIT $6 OFFSET $5
`
type GetOddsByEventIDParams struct {
EventID string `json:"event_id"`
IsLive pgtype.Bool `json:"is_live"`
Status pgtype.Text `json:"status"`
Source pgtype.Text `json:"source"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
func (q *Queries) GetOddsByEventID(ctx context.Context, arg GetOddsByEventIDParams) ([]OddsMarketWithEvent, error) {
rows, err := q.db.Query(ctx, GetOddsByEventID,
arg.EventID,
arg.IsLive,
arg.Status,
arg.Source,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OddsMarketWithEvent
for rows.Next() {
var i OddsMarketWithEvent
if err := rows.Scan(
&i.ID,
&i.EventID,
&i.MarketType,
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
&i.IsMonitored,
&i.IsLive,
&i.Status,
&i.Source,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -67,89 +186,83 @@ func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]Odd, error) {
} }
const GetOddsByMarketID = `-- name: GetOddsByMarketID :one const GetOddsByMarketID = `-- name: GetOddsByMarketID :one
SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, expires_at, source, is_active SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source
FROM odds FROM odds_market_with_event
WHERE market_id = $1 WHERE market_id = $1
AND fi = $2 AND event_id = $2
AND is_active = true
AND source = 'bet365'
` `
type GetOddsByMarketIDParams struct { type GetOddsByMarketIDParams struct {
MarketID pgtype.Text `json:"market_id"` MarketID string `json:"market_id"`
Fi pgtype.Text `json:"fi"` EventID string `json:"event_id"`
} }
func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDParams) (Odd, error) { func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDParams) (OddsMarketWithEvent, error) {
row := q.db.QueryRow(ctx, GetOddsByMarketID, arg.MarketID, arg.Fi) row := q.db.QueryRow(ctx, GetOddsByMarketID, arg.MarketID, arg.EventID)
var i Odd var i OddsMarketWithEvent
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.EventID, &i.EventID,
&i.Fi,
&i.MarketType, &i.MarketType,
&i.MarketName, &i.MarketName,
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.Name,
&i.Handicap,
&i.OddsValue,
&i.Section,
&i.Category,
&i.RawOdds, &i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
&i.IsMonitored,
&i.IsLive,
&i.Status,
&i.Source, &i.Source,
&i.IsActive,
) )
return i, err return i, err
} }
const GetPaginatedPrematchOddsByUpcomingID = `-- name: GetPaginatedPrematchOddsByUpcomingID :many const GetOddsWithSettingsByEventID = `-- name: GetOddsWithSettingsByEventID :many
SELECT o.id, o.event_id, o.fi, o.market_type, o.market_name, o.market_category, o.market_id, o.name, o.handicap, o.odds_value, o.section, o.category, o.raw_odds, o.fetched_at, o.expires_at, o.source, o.is_active SELECT id, event_id, market_type, market_name, market_category, market_id, odds_market_with_settings.raw_odds, default_is_active, fetched_at, expires_at, company_id, is_active, odds_market_with_settings.raw_odds, updated_at
FROM odds o FROM odds_market_with_settings
JOIN events e ON o.fi = e.id WHERE event_id = $1
WHERE e.id = $1 AND company_id = $2
AND e.is_live = false LIMIT $4 OFFSET $3
AND e.status = 'upcoming'
AND o.is_active = true
AND o.source = 'bet365'
LIMIT $3 OFFSET $2
` `
type GetPaginatedPrematchOddsByUpcomingIDParams struct { type GetOddsWithSettingsByEventIDParams struct {
ID string `json:"id"` EventID string `json:"event_id"`
CompanyID int64 `json:"company_id"`
Offset pgtype.Int4 `json:"offset"` Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"` Limit pgtype.Int4 `json:"limit"`
} }
func (q *Queries) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, arg GetPaginatedPrematchOddsByUpcomingIDParams) ([]Odd, error) { func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsWithSettingsByEventIDParams) ([]OddsMarketWithSetting, error) {
rows, err := q.db.Query(ctx, GetPaginatedPrematchOddsByUpcomingID, arg.ID, arg.Offset, arg.Limit) rows, err := q.db.Query(ctx, GetOddsWithSettingsByEventID,
arg.EventID,
arg.CompanyID,
arg.Offset,
arg.Limit,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []Odd var items []OddsMarketWithSetting
for rows.Next() { for rows.Next() {
var i Odd var i OddsMarketWithSetting
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.EventID, &i.EventID,
&i.Fi,
&i.MarketType, &i.MarketType,
&i.MarketName, &i.MarketName,
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.Name,
&i.Handicap,
&i.OddsValue,
&i.Section,
&i.Category,
&i.RawOdds, &i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
&i.Source, &i.CompanyID,
&i.IsActive, &i.IsActive,
&i.RawOdds,
&i.UpdatedAt,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -161,116 +274,80 @@ func (q *Queries) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, arg
return items, nil return items, nil
} }
const GetPrematchOdds = `-- name: GetPrematchOdds :many const GetOddsWithSettingsByMarketID = `-- name: GetOddsWithSettingsByMarketID :one
SELECT id, event_id, fi, market_type, market_name, market_category, market_id, name, handicap, odds_value, section, category, raw_odds, fetched_at, expires_at, source, is_active SELECT id, event_id, market_type, market_name, market_category, market_id, odds_market_with_settings.raw_odds, default_is_active, fetched_at, expires_at, company_id, is_active, odds_market_with_settings.raw_odds, updated_at
FROM odds FROM odds_market_with_settings
WHERE is_active = true WHERE market_id = $1
AND source = 'bet365' AND event_id = $2
AND company_id = $3
` `
func (q *Queries) GetPrematchOdds(ctx context.Context) ([]Odd, error) { type GetOddsWithSettingsByMarketIDParams struct {
rows, err := q.db.Query(ctx, GetPrematchOdds) MarketID string `json:"market_id"`
if err != nil { EventID string `json:"event_id"`
return nil, err CompanyID int64 `json:"company_id"`
} }
defer rows.Close()
var items []Odd func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (OddsMarketWithSetting, error) {
for rows.Next() { row := q.db.QueryRow(ctx, GetOddsWithSettingsByMarketID, arg.MarketID, arg.EventID, arg.CompanyID)
var i Odd var i OddsMarketWithSetting
if err := rows.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.EventID, &i.EventID,
&i.Fi,
&i.MarketType, &i.MarketType,
&i.MarketName, &i.MarketName,
&i.MarketCategory, &i.MarketCategory,
&i.MarketID, &i.MarketID,
&i.Name,
&i.Handicap,
&i.OddsValue,
&i.Section,
&i.Category,
&i.RawOdds, &i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt, &i.FetchedAt,
&i.ExpiresAt, &i.ExpiresAt,
&i.Source, &i.CompanyID,
&i.IsActive, &i.IsActive,
); err != nil { &i.RawOdds,
return nil, err &i.UpdatedAt,
} )
items = append(items, i) return i, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
} }
const GetPrematchOddsByUpcomingID = `-- name: GetPrematchOddsByUpcomingID :many const InsertOddSettings = `-- name: InsertOddSettings :exec
SELECT o.id, o.event_id, o.fi, o.market_type, o.market_name, o.market_category, o.market_id, o.name, o.handicap, o.odds_value, o.section, o.category, o.raw_odds, o.fetched_at, o.expires_at, o.source, o.is_active INSERT INTO company_odd_settings (
FROM odds o company_id,
JOIN events e ON o.fi = e.id odds_market_id,
WHERE e.id = $1 is_active,
AND e.is_live = false custom_raw_odds
AND e.status = 'upcoming' )
AND o.is_active = true VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO
AND o.source = 'bet365' UPDATE
SET is_active = EXCLUDED.is_active,
custom_raw_odds = EXCLUDED.custom_raw_odds
` `
func (q *Queries) GetPrematchOddsByUpcomingID(ctx context.Context, id string) ([]Odd, error) { type InsertOddSettingsParams struct {
rows, err := q.db.Query(ctx, GetPrematchOddsByUpcomingID, id) CompanyID int64 `json:"company_id"`
if err != nil { OddsMarketID int64 `json:"odds_market_id"`
return nil, err IsActive pgtype.Bool `json:"is_active"`
} CustomRawOdds []byte `json:"custom_raw_odds"`
defer rows.Close()
var items []Odd
for rows.Next() {
var i Odd
if err := rows.Scan(
&i.ID,
&i.EventID,
&i.Fi,
&i.MarketType,
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.Name,
&i.Handicap,
&i.OddsValue,
&i.Section,
&i.Category,
&i.RawOdds,
&i.FetchedAt,
&i.ExpiresAt,
&i.Source,
&i.IsActive,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
} }
const InsertNonLiveOdd = `-- name: InsertNonLiveOdd :exec func (q *Queries) InsertOddSettings(ctx context.Context, arg InsertOddSettingsParams) error {
INSERT INTO odds ( _, err := q.db.Exec(ctx, InsertOddSettings,
arg.CompanyID,
arg.OddsMarketID,
arg.IsActive,
arg.CustomRawOdds,
)
return err
}
const InsertOddsMarket = `-- name: InsertOddsMarket :exec
INSERT INTO odds_market (
event_id, event_id,
fi,
market_type, market_type,
market_name, market_name,
market_category, market_category,
market_id, market_id,
name,
handicap,
odds_value,
section,
category,
raw_odds, raw_odds,
is_active,
source,
fetched_at, fetched_at,
expires_at expires_at
) )
@ -282,65 +359,36 @@ VALUES (
$5, $5,
$6, $6,
$7, $7,
$8, $8
$9,
$10,
$11,
$12,
$13,
$14,
$15,
$16
) ON CONFLICT (event_id, market_id) DO ) ON CONFLICT (event_id, market_id) DO
UPDATE UPDATE
SET odds_value = EXCLUDED.odds_value, SET market_type = EXCLUDED.market_type,
raw_odds = EXCLUDED.raw_odds,
market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name, market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category, market_category = EXCLUDED.market_category,
name = EXCLUDED.name, raw_odds = EXCLUDED.raw_odds,
handicap = EXCLUDED.handicap,
fetched_at = EXCLUDED.fetched_at, fetched_at = EXCLUDED.fetched_at,
is_active = EXCLUDED.is_active, expires_at = EXCLUDED.expires_at
source = EXCLUDED.source,
fi = EXCLUDED.fi
` `
type InsertNonLiveOddParams struct { type InsertOddsMarketParams struct {
EventID pgtype.Text `json:"event_id"` EventID string `json:"event_id"`
Fi pgtype.Text `json:"fi"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName pgtype.Text `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory pgtype.Text `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID pgtype.Text `json:"market_id"` MarketID string `json:"market_id"`
Name pgtype.Text `json:"name"`
Handicap pgtype.Text `json:"handicap"`
OddsValue pgtype.Float8 `json:"odds_value"`
Section string `json:"section"`
Category pgtype.Text `json:"category"`
RawOdds []byte `json:"raw_odds"` RawOdds []byte `json:"raw_odds"`
IsActive pgtype.Bool `json:"is_active"`
Source pgtype.Text `json:"source"`
FetchedAt pgtype.Timestamp `json:"fetched_at"` FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"` ExpiresAt pgtype.Timestamp `json:"expires_at"`
} }
func (q *Queries) InsertNonLiveOdd(ctx context.Context, arg InsertNonLiveOddParams) error { func (q *Queries) InsertOddsMarket(ctx context.Context, arg InsertOddsMarketParams) error {
_, err := q.db.Exec(ctx, InsertNonLiveOdd, _, err := q.db.Exec(ctx, InsertOddsMarket,
arg.EventID, arg.EventID,
arg.Fi,
arg.MarketType, arg.MarketType,
arg.MarketName, arg.MarketName,
arg.MarketCategory, arg.MarketCategory,
arg.MarketID, arg.MarketID,
arg.Name,
arg.Handicap,
arg.OddsValue,
arg.Section,
arg.Category,
arg.RawOdds, arg.RawOdds,
arg.IsActive,
arg.Source,
arg.FetchedAt, arg.FetchedAt,
arg.ExpiresAt, arg.ExpiresAt,
) )

View File

@ -27,7 +27,7 @@ func (q *Queries) CountTicketByIP(ctx context.Context, ip string) (int64, error)
const CreateTicket = `-- name: CreateTicket :one const CreateTicket = `-- name: CreateTicket :one
INSERT INTO tickets (amount, total_odds, ip) INSERT INTO tickets (amount, total_odds, ip)
VALUES ($1, $2, $3) VALUES ($1, $2, $3)
RETURNING id, amount, total_odds, ip, created_at, updated_at RETURNING id, company_id, amount, total_odds, ip, created_at, updated_at
` `
type CreateTicketParams struct { type CreateTicketParams struct {
@ -41,6 +41,7 @@ func (q *Queries) CreateTicket(ctx context.Context, arg CreateTicketParams) (Tic
var i Ticket var i Ticket
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Ip, &i.Ip,
@ -96,7 +97,7 @@ func (q *Queries) DeleteTicketOutcome(ctx context.Context, ticketID int64) error
} }
const GetAllTickets = `-- name: GetAllTickets :many const GetAllTickets = `-- name: GetAllTickets :many
SELECT id, amount, total_odds, ip, created_at, updated_at, outcomes SELECT id, company_id, amount, total_odds, ip, created_at, updated_at, outcomes
FROM ticket_with_outcomes FROM ticket_with_outcomes
` `
@ -111,6 +112,7 @@ func (q *Queries) GetAllTickets(ctx context.Context) ([]TicketWithOutcome, error
var i TicketWithOutcome var i TicketWithOutcome
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Ip, &i.Ip,
@ -152,7 +154,7 @@ func (q *Queries) GetAllTicketsInRange(ctx context.Context, arg GetAllTicketsInR
} }
const GetTicketByID = `-- name: GetTicketByID :one const GetTicketByID = `-- name: GetTicketByID :one
SELECT id, amount, total_odds, ip, created_at, updated_at, outcomes SELECT id, company_id, amount, total_odds, ip, created_at, updated_at, outcomes
FROM ticket_with_outcomes FROM ticket_with_outcomes
WHERE id = $1 WHERE id = $1
` `
@ -162,6 +164,7 @@ func (q *Queries) GetTicketByID(ctx context.Context, id int64) (TicketWithOutcom
var i TicketWithOutcome var i TicketWithOutcome
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.CompanyID,
&i.Amount, &i.Amount,
&i.TotalOdds, &i.TotalOdds,
&i.Ip, &i.Ip,

View File

@ -17,17 +17,20 @@ SELECT EXISTS (
FROM users FROM users
WHERE users.phone_number = $1 WHERE users.phone_number = $1
AND users.phone_number IS NOT NULL AND users.phone_number IS NOT NULL
AND users.company_id = $2
) AS phone_exists, ) AS phone_exists,
EXISTS ( EXISTS (
SELECT 1 SELECT 1
FROM users FROM users
WHERE users.email = $2 WHERE users.email = $3
AND users.email IS NOT NULL AND users.email IS NOT NULL
AND users.company_id = $2
) AS email_exists ) AS email_exists
` `
type CheckPhoneEmailExistParams struct { type CheckPhoneEmailExistParams struct {
PhoneNumber pgtype.Text `json:"phone_number"` PhoneNumber pgtype.Text `json:"phone_number"`
CompanyID pgtype.Int8 `json:"company_id"`
Email pgtype.Text `json:"email"` Email pgtype.Text `json:"email"`
} }
@ -37,7 +40,7 @@ type CheckPhoneEmailExistRow struct {
} }
func (q *Queries) CheckPhoneEmailExist(ctx context.Context, arg CheckPhoneEmailExistParams) (CheckPhoneEmailExistRow, error) { func (q *Queries) CheckPhoneEmailExist(ctx context.Context, arg CheckPhoneEmailExistParams) (CheckPhoneEmailExistRow, error) {
row := q.db.QueryRow(ctx, CheckPhoneEmailExist, arg.PhoneNumber, arg.Email) row := q.db.QueryRow(ctx, CheckPhoneEmailExist, arg.PhoneNumber, arg.CompanyID, arg.Email)
var i CheckPhoneEmailExistRow var i CheckPhoneEmailExistRow
err := row.Scan(&i.PhoneExists, &i.EmailExists) err := row.Scan(&i.PhoneExists, &i.EmailExists)
return i, err return i, err
@ -339,8 +342,14 @@ SELECT id,
company_id company_id
FROM users FROM users
WHERE email = $1 WHERE email = $1
AND company_id = $2
` `
type GetUserByEmailParams struct {
Email pgtype.Text `json:"email"`
CompanyID pgtype.Int8 `json:"company_id"`
}
type GetUserByEmailRow struct { type GetUserByEmailRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
FirstName string `json:"first_name"` FirstName string `json:"first_name"`
@ -357,8 +366,8 @@ type GetUserByEmailRow struct {
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
} }
func (q *Queries) GetUserByEmail(ctx context.Context, email pgtype.Text) (GetUserByEmailRow, error) { func (q *Queries) GetUserByEmail(ctx context.Context, arg GetUserByEmailParams) (GetUserByEmailRow, error) {
row := q.db.QueryRow(ctx, GetUserByEmail, email) row := q.db.QueryRow(ctx, GetUserByEmail, arg.Email, arg.CompanyID)
var i GetUserByEmailRow var i GetUserByEmailRow
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
@ -424,8 +433,14 @@ SELECT id,
company_id company_id
FROM users FROM users
WHERE phone_number = $1 WHERE phone_number = $1
AND company_id = $2
` `
type GetUserByPhoneParams struct {
PhoneNumber pgtype.Text `json:"phone_number"`
CompanyID pgtype.Int8 `json:"company_id"`
}
type GetUserByPhoneRow struct { type GetUserByPhoneRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
FirstName string `json:"first_name"` FirstName string `json:"first_name"`
@ -442,8 +457,8 @@ type GetUserByPhoneRow struct {
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
} }
func (q *Queries) GetUserByPhone(ctx context.Context, phoneNumber pgtype.Text) (GetUserByPhoneRow, error) { func (q *Queries) GetUserByPhone(ctx context.Context, arg GetUserByPhoneParams) (GetUserByPhoneRow, error) {
row := q.db.QueryRow(ctx, GetUserByPhone, phoneNumber) row := q.db.QueryRow(ctx, GetUserByPhone, arg.PhoneNumber, arg.CompanyID)
var i GetUserByPhoneRow var i GetUserByPhoneRow
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
@ -478,25 +493,22 @@ SELECT id,
suspended_at, suspended_at,
company_id company_id
FROM users FROM users
WHERE ( WHERE (company_id = $1)
first_name ILIKE '%' || $1 || '%' AND (
OR last_name ILIKE '%' || $1 || '%' first_name ILIKE '%' || $2 || '%'
OR phone_number LIKE '%' || $1 || '%' OR last_name ILIKE '%' || $2 || '%'
OR phone_number LIKE '%' || $2 || '%'
) )
AND ( AND (
role = $2 role = $3
OR $2 IS NULL
)
AND (
company_id = $3
OR $3 IS NULL OR $3 IS NULL
) )
` `
type SearchUserByNameOrPhoneParams struct { type SearchUserByNameOrPhoneParams struct {
Column1 pgtype.Text `json:"column_1"`
Role pgtype.Text `json:"role"`
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
Column2 pgtype.Text `json:"column_2"`
Role pgtype.Text `json:"role"`
} }
type SearchUserByNameOrPhoneRow struct { type SearchUserByNameOrPhoneRow struct {
@ -516,7 +528,7 @@ type SearchUserByNameOrPhoneRow struct {
} }
func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByNameOrPhoneParams) ([]SearchUserByNameOrPhoneRow, error) { func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByNameOrPhoneParams) ([]SearchUserByNameOrPhoneRow, error) {
rows, err := q.db.Query(ctx, SearchUserByNameOrPhone, arg.Column1, arg.Role, arg.CompanyID) rows, err := q.db.Query(ctx, SearchUserByNameOrPhone, arg.CompanyID, arg.Column2, arg.Role)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -575,6 +587,7 @@ SET password = $1,
WHERE ( WHERE (
email = $2 email = $2
OR phone_number = $3 OR phone_number = $3
AND company_id = $4
) )
` `

View File

@ -50,7 +50,7 @@ INSERT INTO wallets (
type type
) )
VALUES ($1, $2, $3, $4, $5) VALUES ($1, $2, $3, $4, $5)
RETURNING id, balance, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, currency, bonus_balance, cash_balance RETURNING id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance
` `
type CreateWalletParams struct { type CreateWalletParams struct {
@ -73,6 +73,7 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Balance, &i.Balance,
&i.Currency,
&i.IsWithdraw, &i.IsWithdraw,
&i.IsBettable, &i.IsBettable,
&i.IsTransferable, &i.IsTransferable,
@ -81,7 +82,6 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Currency,
&i.BonusBalance, &i.BonusBalance,
&i.CashBalance, &i.CashBalance,
) )
@ -188,7 +188,7 @@ func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDet
} }
const GetAllWallets = `-- name: GetAllWallets :many const GetAllWallets = `-- name: GetAllWallets :many
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, currency, bonus_balance, cash_balance SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance
FROM wallets FROM wallets
` `
@ -204,6 +204,7 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Balance, &i.Balance,
&i.Currency,
&i.IsWithdraw, &i.IsWithdraw,
&i.IsBettable, &i.IsBettable,
&i.IsTransferable, &i.IsTransferable,
@ -212,7 +213,6 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Currency,
&i.BonusBalance, &i.BonusBalance,
&i.CashBalance, &i.CashBalance,
); err != nil { ); err != nil {
@ -319,7 +319,7 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (Cust
} }
const GetWalletByID = `-- name: GetWalletByID :one const GetWalletByID = `-- name: GetWalletByID :one
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, currency, bonus_balance, cash_balance SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance
FROM wallets FROM wallets
WHERE id = $1 WHERE id = $1
` `
@ -330,6 +330,7 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Balance, &i.Balance,
&i.Currency,
&i.IsWithdraw, &i.IsWithdraw,
&i.IsBettable, &i.IsBettable,
&i.IsTransferable, &i.IsTransferable,
@ -338,7 +339,6 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Currency,
&i.BonusBalance, &i.BonusBalance,
&i.CashBalance, &i.CashBalance,
) )
@ -346,7 +346,7 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) {
} }
const GetWalletByUserID = `-- name: GetWalletByUserID :many const GetWalletByUserID = `-- name: GetWalletByUserID :many
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, currency, bonus_balance, cash_balance SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance
FROM wallets FROM wallets
WHERE user_id = $1 WHERE user_id = $1
` `
@ -363,6 +363,7 @@ func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Balance, &i.Balance,
&i.Currency,
&i.IsWithdraw, &i.IsWithdraw,
&i.IsBettable, &i.IsBettable,
&i.IsTransferable, &i.IsTransferable,
@ -371,7 +372,6 @@ func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.Currency,
&i.BonusBalance, &i.BonusBalance,
&i.CashBalance, &i.CashBalance,
); err != nil { ); err != nil {

View File

@ -0,0 +1,34 @@
package domain
import (
"time"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
// var BadRequestZapFields = []zap.Field{
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Time("timestamp", time.Now()),
// }
// var InternalServerErrorZapFields = []zap.Field{
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Time("timestamp", time.Now()),
// }
var SuccessResZapFields = []zap.Field{
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
}
var BadRequestLogger = MongoDBLogger.With(
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()))
var InternalServerErrorLogger = MongoDBLogger.With(
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()))
var SuccessResLogger = MongoDBLogger.With(
zap.Int("status_code", fiber.StatusOK),
zap.Time("timestamp", time.Now()))

View File

@ -11,6 +11,7 @@ import (
type Company struct { type Company struct {
ID int64 ID int64
Name string Name string
Slug string
AdminID int64 AdminID int64
WalletID int64 WalletID int64
DeductedPercentage float32 DeductedPercentage float32
@ -27,6 +28,7 @@ type CompanyFilter struct {
type GetCompany struct { type GetCompany struct {
ID int64 ID int64
Name string Name string
Slug string
AdminID int64 AdminID int64
AdminFirstName string AdminFirstName string
AdminLastName string AdminLastName string
@ -68,6 +70,7 @@ type UpdateCompanyReq struct {
type CompanyRes struct { type CompanyRes struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
Name string `json:"name" example:"CompanyName"` Name string `json:"name" example:"CompanyName"`
Slug string `json:"slug" example:"slug"`
AdminID int64 `json:"admin_id" example:"1"` AdminID int64 `json:"admin_id" example:"1"`
WalletID int64 `json:"wallet_id" example:"1"` WalletID int64 `json:"wallet_id" example:"1"`
DeductedPercentage float32 `json:"deducted_percentage" example:"0.1"` DeductedPercentage float32 `json:"deducted_percentage" example:"0.1"`
@ -77,6 +80,7 @@ type CompanyRes struct {
type GetCompanyRes struct { type GetCompanyRes struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
Name string `json:"name" example:"CompanyName"` Name string `json:"name" example:"CompanyName"`
Slug string `json:"slug" example:"slug"`
AdminID int64 `json:"admin_id" example:"1"` AdminID int64 `json:"admin_id" example:"1"`
WalletID int64 `json:"wallet_id" example:"1"` WalletID int64 `json:"wallet_id" example:"1"`
WalletBalance float32 `json:"balance" example:"1"` WalletBalance float32 `json:"balance" example:"1"`
@ -92,6 +96,7 @@ func ConvertCompany(company Company) CompanyRes {
return CompanyRes{ return CompanyRes{
ID: company.ID, ID: company.ID,
Name: company.Name, Name: company.Name,
Slug: company.Slug,
AdminID: company.AdminID, AdminID: company.AdminID,
WalletID: company.WalletID, WalletID: company.WalletID,
IsActive: company.IsActive, IsActive: company.IsActive,
@ -103,6 +108,7 @@ func ConvertGetCompany(company GetCompany) GetCompanyRes {
return GetCompanyRes{ return GetCompanyRes{
ID: company.ID, ID: company.ID,
Name: company.Name, Name: company.Name,
Slug: company.Slug,
AdminID: company.AdminID, AdminID: company.AdminID,
WalletID: company.WalletID, WalletID: company.WalletID,
WalletBalance: company.WalletBalance.Float32(), WalletBalance: company.WalletBalance.Float32(),
@ -112,13 +118,13 @@ func ConvertGetCompany(company GetCompany) GetCompanyRes {
AdminFirstName: company.AdminFirstName, AdminFirstName: company.AdminFirstName,
AdminLastName: company.AdminLastName, AdminLastName: company.AdminLastName,
AdminPhoneNumber: company.AdminPhoneNumber, AdminPhoneNumber: company.AdminPhoneNumber,
} }
} }
func ConvertCreateCompany(company CreateCompany) dbgen.CreateCompanyParams { func ConvertCreateCompany(company CreateCompany, uniqueSlug string) dbgen.CreateCompanyParams {
return dbgen.CreateCompanyParams{ return dbgen.CreateCompanyParams{
Name: company.Name, Name: company.Name,
Slug: uniqueSlug,
AdminID: company.AdminID, AdminID: company.AdminID,
WalletID: company.WalletID, WalletID: company.WalletID,
DeductedPercentage: company.DeductedPercentage, DeductedPercentage: company.DeductedPercentage,
@ -129,6 +135,7 @@ func ConvertDBCompany(dbCompany dbgen.Company) Company {
return Company{ return Company{
ID: dbCompany.ID, ID: dbCompany.ID,
Name: dbCompany.Name, Name: dbCompany.Name,
Slug: dbCompany.Slug,
AdminID: dbCompany.AdminID, AdminID: dbCompany.AdminID,
WalletID: dbCompany.WalletID, WalletID: dbCompany.WalletID,
DeductedPercentage: dbCompany.DeductedPercentage, DeductedPercentage: dbCompany.DeductedPercentage,
@ -140,6 +147,7 @@ func ConvertDBCompanyDetails(dbCompany dbgen.CompaniesDetail) GetCompany {
return GetCompany{ return GetCompany{
ID: dbCompany.ID, ID: dbCompany.ID,
Name: dbCompany.Name, Name: dbCompany.Name,
Slug: dbCompany.Slug,
AdminID: dbCompany.AdminID, AdminID: dbCompany.AdminID,
WalletID: dbCompany.WalletID, WalletID: dbCompany.WalletID,
WalletBalance: Currency(dbCompany.Balance), WalletBalance: Currency(dbCompany.Balance),

View File

@ -1,6 +1,7 @@
package domain package domain
import ( import (
"encoding/json"
"time" "time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
@ -9,35 +10,60 @@ import (
type CustomOdd struct { type CustomOdd struct {
ID int64 ID int64
OddID int64 OddID int64
RawOddID int64 CompanyID int64
EventID string EventID string
OddValue float64 RawOdds []json.RawMessage
CreatedAt time.Time CreatedAt time.Time
} }
type CreateCustomOdd struct { type CreateCustomOdd struct {
OddID int64 OddID int64
RawOddID int64 CompanyID int64
EventID string EventID string
OddValue float64 RawOdds []json.RawMessage
} }
func ConvertCreateCustomOdd(odd CreateCustomOdd) dbgen.InsertCustomOddParams { type CustomOddFilter struct {
CompanyID ValidInt64
}
func ConvertCreateCustomOdd(odd CreateCustomOdd) (dbgen.InsertCustomOddParams, error) {
rawOddsBytes, err := json.Marshal(odd.RawOdds)
if err != nil {
return dbgen.InsertCustomOddParams{}, err
}
return dbgen.InsertCustomOddParams{ return dbgen.InsertCustomOddParams{
OddID: odd.OddID, OddID: odd.OddID,
RawOddID: odd.RawOddID, CompanyID: odd.CompanyID,
EventID: odd.EventID, EventID: odd.EventID,
OddValue: odd.OddValue, RawOdds: rawOddsBytes,
} }, nil
} }
func ConvertDBCustomOdd(dbCustomOdd dbgen.CustomOdd) CustomOdd { func ConvertDBCustomOdd(dbCustomOdd dbgen.CustomOdd) (CustomOdd, error) {
var rawOdds []json.RawMessage
if err := json.Unmarshal(dbCustomOdd.RawOdds, &rawOdds); err != nil {
return CustomOdd{}, err
}
return CustomOdd{ return CustomOdd{
ID: dbCustomOdd.ID, ID: dbCustomOdd.ID,
OddID: dbCustomOdd.OddID, OddID: dbCustomOdd.OddID,
RawOddID: dbCustomOdd.RawOddID, CompanyID: dbCustomOdd.CompanyID,
EventID: dbCustomOdd.EventID, EventID: dbCustomOdd.EventID,
OddValue: dbCustomOdd.OddValue, RawOdds: rawOdds,
CreatedAt: dbCustomOdd.CreatedAt.Time, CreatedAt: dbCustomOdd.CreatedAt.Time,
} }, nil
}
func ConvertDbCustomOdds(list []dbgen.CustomOdd) ([]CustomOdd, error) {
result := make([]CustomOdd, 0, len(list))
for _, item := range list {
convertedItem, err := ConvertDBCustomOdd(item)
if err != nil {
return nil, err
}
result = append(result, convertedItem)
}
return result, nil
} }

View File

@ -11,6 +11,7 @@ type DisabledOdd struct {
OddID int64 OddID int64
RawOddID int64 RawOddID int64
EventID string EventID string
CompanyID int64
CreatedAt time.Time CreatedAt time.Time
} }
@ -18,12 +19,14 @@ type CreateDisabledOdd struct {
OddID int64 OddID int64
RawOddID int64 RawOddID int64
EventID string EventID string
CompanyID int64
} }
func ConvertCreateDisabledOdd(odd CreateDisabledOdd) dbgen.InsertDisabledOddsParams { func ConvertCreateDisabledOdd(odd CreateDisabledOdd) dbgen.InsertDisabledOddsParams {
return dbgen.InsertDisabledOddsParams{ return dbgen.InsertDisabledOddsParams{
OddID: odd.OddID, OddID: odd.OddID,
EventID: odd.EventID, EventID: odd.EventID,
CompanyID: odd.CompanyID,
RawOddID: odd.RawOddID, RawOddID: odd.RawOddID,
} }
} }
@ -34,6 +37,7 @@ func ConvertDBDisabledOdd(dbDisabledOdd dbgen.DisabledOdd) DisabledOdd {
OddID: dbDisabledOdd.OddID, OddID: dbDisabledOdd.OddID,
RawOddID: dbDisabledOdd.RawOddID, RawOddID: dbDisabledOdd.RawOddID,
EventID: dbDisabledOdd.EventID, EventID: dbDisabledOdd.EventID,
CompanyID: dbDisabledOdd.CompanyID,
CreatedAt: dbDisabledOdd.CreatedAt.Time, CreatedAt: dbDisabledOdd.CreatedAt.Time,
} }
} }

View File

@ -1,6 +1,11 @@
package domain package domain
import "time" import (
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/jackc/pgx/v5/pgtype"
)
// TODO: turn status into an enum // TODO: turn status into an enum
// Status represents the status of an event. // Status represents the status of an event.
@ -35,93 +40,408 @@ const (
STATUS_REMOVED EventStatus = "removed" STATUS_REMOVED EventStatus = "removed"
) )
type Event struct { type EventSource string
const (
EVENT_SOURCE_BET365 EventSource = "b365api"
EVENT_SOURCE_BWIN EventSource = "bwin"
EVENT_SOURCE_BETFAIR EventSource = "bfair"
EVENT_SOURCE_1XBET EventSource = "1xbet"
EVENT_SOURCE_ENET EventSource = "enetpulse"
)
type BaseEvent struct {
ID string ID string
SportID int32 SportID int32
MatchName string MatchName string
HomeTeam string HomeTeam string
AwayTeam string AwayTeam string
HomeTeamID int32 HomeTeamID int64
AwayTeamID int32 AwayTeamID int64
HomeKitImage string HomeTeamImage string
AwayKitImage string AwayTeamImage string
LeagueID int32 LeagueID int64
LeagueName string LeagueName string
LeagueCC string LeagueCC ValidString
StartTime string StartTime time.Time
Score string Source EventSource
MatchMinute int Status EventStatus
TimerStatus string IsMonitored bool
AddedTime int DefaultIsFeatured bool
MatchPeriod int DefaultIsActive bool
DefaultWinningUpperLimit int32
Score ValidString
MatchMinute ValidInt
TimerStatus ValidString
AddedTime ValidInt
MatchPeriod ValidInt
IsLive bool IsLive bool
Status string FetchedAt time.Time
Source string }
type BaseEventRes struct {
ID string `json:"id"`
SportID int32 `json:"sport_id"`
MatchName string `json:"match_name"`
HomeTeam string `json:"home_team"`
AwayTeam string `json:"away_team"`
HomeTeamID int64 `json:"home_team_id"`
AwayTeamID int64 `json:"away_team_id"`
HomeTeamImage string `json:"home_team_image"`
AwayTeamImage string `json:"away_team_image"`
LeagueID int64 `json:"league_id"`
LeagueName string `json:"league_name"`
LeagueCC string `json:"league_cc"`
StartTime time.Time `json:"start_time"`
Source EventSource `json:"source"`
Status EventStatus `json:"status"`
IsMonitored bool `json:"is_monitored"`
DefaultIsFeatured bool `json:"default_is_featured"`
DefaultIsActive bool `json:"default_is_active"`
DefaultWinningUpperLimit int32 `json:"default_winning_upper_limit"`
Score string `json:"score,omitempty"`
MatchMinute int `json:"match_minute,omitempty"`
TimerStatus string `json:"timer_status,omitempty"`
AddedTime int `json:"added_time,omitempty"`
MatchPeriod int `json:"match_period,omitempty"`
IsLive bool `json:"is_live"`
FetchedAt time.Time `json:"fetched_at"`
}
type EventWithSettings struct {
ID string
SportID int32
MatchName string
HomeTeam string
AwayTeam string
HomeTeamID int64
AwayTeamID int64
HomeTeamImage string
AwayTeamImage string
LeagueID int64
LeagueName string
LeagueCC ValidString
StartTime time.Time
Source EventSource
Status EventStatus
IsFeatured bool
IsMonitored bool
IsActive bool
WinningUpperLimit int32
Score ValidString
MatchMinute ValidInt
TimerStatus ValidString
AddedTime ValidInt
MatchPeriod ValidInt
IsLive bool
UpdatedAt time.Time
FetchedAt time.Time
} }
type BetResult struct { type CreateEvent struct {
Success int `json:"success"` ID string
Pager struct { SportID int32
Page int `json:"page"` MatchName string
PerPage int `json:"per_page"` HomeTeam string
Total int `json:"total"` AwayTeam string
} HomeTeamID int64
Results []struct { AwayTeamID int64
ID string `json:"id"` HomeTeamImage string
SportID string `json:"sport_id"` AwayTeamImage string
Time string `json:"time"` LeagueID int64
League struct { LeagueName string
ID string `json:"id"` StartTime time.Time
Name string `json:"name"` IsLive bool
} `json:"league"` Status EventStatus
Home struct { Source EventSource
ID string `json:"id"`
Name string `json:"name"`
} `json:"home"`
Away *struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"away"`
} `json:"results"`
} }
type UpcomingEvent struct { type EventWithSettingsRes struct {
ID string `json:"id"` // Event ID ID string `json:"id"`
SportID int32 `json:"sport_id"` // Sport ID SportID int32 `json:"sport_id"`
MatchName string `json:"match_name"` // Match or event name MatchName string `json:"match_name"`
HomeTeam string `json:"home_team"` // Home team name (if available) HomeTeam string `json:"home_team"`
AwayTeam string `json:"away_team"` // Away team name (can be empty/null) AwayTeam string `json:"away_team"`
HomeTeamID int32 `json:"home_team_id"` // Home team ID HomeTeamID int64 `json:"home_team_id"`
AwayTeamID int32 `json:"away_team_id"` // Away team ID (can be empty/null) AwayTeamID int64 `json:"away_team_id"`
HomeKitImage string `json:"home_kit_image"` // Kit or image for home team (optional) HomeTeamImage string `json:"home_team_image"`
AwayKitImage string `json:"away_kit_image"` // Kit or image for away team (optional) AwayTeamImage string `json:"away_team_image"`
LeagueID int32 `json:"league_id"` // League ID LeagueID int64 `json:"league_id"`
LeagueName string `json:"league_name"` // League name LeagueName string `json:"league_name"`
LeagueCC string `json:"league_cc"` // League country code LeagueCC string `json:"league_cc"`
StartTime time.Time `json:"start_time"` // Converted from "time" field in UNIX format StartTime time.Time `json:"start_time"`
Source string `json:"source"` // bet api provider (bet365, betfair) Source EventSource `json:"source"`
Status EventStatus `json:"status"` //Match Status for event Status EventStatus `json:"status"`
IsFeatured bool `json:"is_featured"` //Whether the event is featured or not IsFeatured bool `json:"is_featured"`
IsMonitored bool `json:"is_monitored"` //Whether the event is monitored or not IsMonitored bool `json:"is_monitored"`
IsActive bool `json:"is_active"` //Whether the event is active or not IsActive bool `json:"is_active"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
Score string `json:"score,omitempty"`
MatchMinute int `json:"match_minute,omitempty"`
TimerStatus string `json:"timer_status,omitempty"`
AddedTime int `json:"added_time,omitempty"`
MatchPeriod int `json:"match_period,omitempty"`
IsLive bool `json:"is_live"`
UpdatedAt time.Time `json:"updated_at"`
FetchedAt time.Time `json:"fetched_at"`
} }
type MatchResult struct {
type EventSettings struct {
CompanyID int64
EventID string EventID string
FullScore string IsActive ValidBool
HalfScore string IsFeatured ValidBool
Status string WinningUpperLimit ValidInt
Scores map[string]map[string]string UpdatedAt time.Time
}
type CreateEventSettings struct {
CompanyID int64
EventID string
IsActive ValidBool
IsFeatured ValidBool
WinningUpperLimit ValidInt
} }
type EventFilter struct { type EventFilter struct {
Query ValidString Query ValidString
SportID ValidInt32 SportID ValidInt32
LeagueID ValidInt32 LeagueID ValidInt64
CountryCode ValidString CountryCode ValidString
FirstStartTime ValidTime FirstStartTime ValidTime
LastStartTime ValidTime LastStartTime ValidTime
Limit ValidInt64 Limit ValidInt32
Offset ValidInt64 Offset ValidInt32
MatchStatus ValidString // e.g., "upcoming", "in_play", "ended" MatchStatus ValidString // e.g., "upcoming", "in_play", "ended"
Featured ValidBool Featured ValidBool
} }
func ConvertDBEvent(event dbgen.EventWithCountry) BaseEvent {
return BaseEvent{
ID: event.ID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
AwayTeam: event.AwayTeam,
HomeTeamID: event.HomeTeamID,
AwayTeamID: event.AwayTeamID,
HomeTeamImage: event.HomeKitImage,
AwayTeamImage: event.AwayKitImage,
LeagueID: event.LeagueID,
LeagueName: event.LeagueName,
LeagueCC: ValidString{
Value: event.LeagueCc.String,
Valid: event.LeagueCc.Valid,
},
StartTime: event.StartTime.Time.UTC(),
Source: EventSource(event.Source),
Status: EventStatus(event.Status),
DefaultIsFeatured: event.DefaultIsFeatured,
IsMonitored: event.IsMonitored,
DefaultIsActive: event.DefaultIsActive,
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
Score: ValidString{
Value: event.Score.String,
Valid: event.Score.Valid,
},
MatchMinute: ValidInt{
Value: int(event.MatchMinute.Int32),
Valid: event.MatchMinute.Valid,
},
TimerStatus: ValidString{
Value: event.TimerStatus.String,
Valid: event.TimerStatus.Valid,
},
AddedTime: ValidInt{
Value: int(event.AddedTime.Int32),
Valid: event.AddedTime.Valid,
},
MatchPeriod: ValidInt{
Value: int(event.MatchPeriod.Int32),
Valid: event.MatchPeriod.Valid,
},
IsLive: event.IsLive,
FetchedAt: event.FetchedAt.Time,
}
}
func ConvertDBEvents(events []dbgen.EventWithCountry) []BaseEvent {
result := make([]BaseEvent, len(events))
for i, e := range events {
result[i] = ConvertDBEvent(e)
}
return result
}
func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams {
return dbgen.InsertEventParams{
ID: e.ID,
SportID: e.SportID,
MatchName: e.MatchName,
HomeTeam: e.HomeTeam,
AwayTeam: e.AwayTeam,
HomeTeamID: e.HomeTeamID,
AwayTeamID: e.AwayTeamID,
HomeKitImage: e.HomeTeamImage,
AwayKitImage: e.AwayTeamImage,
LeagueID: e.LeagueID,
LeagueName: e.LeagueName,
StartTime: pgtype.Timestamp{Time: e.StartTime, Valid: true},
IsLive: e.IsLive,
Status: string(e.Status),
Source: string(e.Source),
}
}
func ConvertCreateEventSettings(eventSettings CreateEventSettings) dbgen.InsertEventSettingsParams {
return dbgen.InsertEventSettingsParams{
CompanyID: eventSettings.CompanyID,
EventID: eventSettings.EventID,
IsActive: eventSettings.IsActive.ToPG(),
IsFeatured: eventSettings.IsFeatured.ToPG(),
WinningUpperLimit: eventSettings.WinningUpperLimit.ToPG(),
}
}
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,
LeagueCC: ValidString{
Value: event.LeagueCc.String,
Valid: event.LeagueCc.Valid,
},
StartTime: event.StartTime.Time.UTC(),
Source: EventSource(event.Source),
Status: EventStatus(event.Status),
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
WinningUpperLimit: event.WinningUpperLimit,
Score: ValidString{
Value: event.Score.String,
Valid: event.Score.Valid,
},
MatchMinute: ValidInt{
Value: int(event.MatchMinute.Int32),
Valid: event.MatchMinute.Valid,
},
TimerStatus: ValidString{
Value: event.TimerStatus.String,
Valid: event.TimerStatus.Valid,
},
AddedTime: ValidInt{
Value: int(event.AddedTime.Int32),
Valid: event.AddedTime.Valid,
},
MatchPeriod: ValidInt{
Value: int(event.MatchPeriod.Int32),
Valid: event.MatchPeriod.Valid,
},
IsLive: event.IsLive,
UpdatedAt: event.UpdatedAt.Time,
FetchedAt: event.FetchedAt.Time,
}
}
func ConvertDBEventWithSettings(events []dbgen.EventWithSetting) []EventWithSettings {
result := make([]EventWithSettings, len(events))
for i, e := range events {
result[i] = ConvertDBEventWithSetting(e)
}
return result
}
func ConvertUpdateEventSettings(event CreateEventSettings) dbgen.UpdateEventSettingsParams {
return dbgen.UpdateEventSettingsParams{
EventID: event.EventID,
CompanyID: event.CompanyID,
IsActive: event.IsActive.ToPG(),
IsFeatured: event.IsFeatured.ToPG(),
WinningUpperLimit: event.WinningUpperLimit.ToPG(),
}
}
func ConvertEventRes(event BaseEvent) BaseEventRes {
return BaseEventRes{
ID: event.ID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
AwayTeam: event.AwayTeam,
HomeTeamID: event.HomeTeamID,
AwayTeamID: event.AwayTeamID,
HomeTeamImage: event.HomeTeamImage,
AwayTeamImage: event.AwayTeamImage,
LeagueID: event.LeagueID,
LeagueName: event.LeagueName,
LeagueCC: event.LeagueCC.Value,
StartTime: event.StartTime.UTC(),
Source: EventSource(event.Source),
Status: EventStatus(event.Status),
DefaultIsFeatured: event.DefaultIsFeatured,
IsMonitored: event.IsMonitored,
DefaultIsActive: event.DefaultIsActive,
DefaultWinningUpperLimit: event.DefaultWinningUpperLimit,
Score: event.Score.Value,
MatchMinute: event.MatchMinute.Value,
TimerStatus: event.TimerStatus.Value,
AddedTime: event.AddedTime.Value,
MatchPeriod: event.MatchPeriod.Value,
IsLive: event.IsLive,
FetchedAt: event.FetchedAt.UTC(),
}
}
func ConvertEventResList(events []BaseEvent) []BaseEventRes {
result := make([]BaseEventRes, 0, len(events))
for _, event := range events {
result = append(result, ConvertEventRes(event))
}
return result
}
func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes {
return EventWithSettingsRes{
ID: event.ID,
SportID: event.SportID,
MatchName: event.MatchName,
HomeTeam: event.HomeTeam,
AwayTeam: event.AwayTeam,
HomeTeamID: event.HomeTeamID,
AwayTeamID: event.AwayTeamID,
HomeTeamImage: event.HomeTeamImage,
AwayTeamImage: event.AwayTeamImage,
LeagueID: event.LeagueID,
LeagueName: event.LeagueName,
LeagueCC: event.LeagueCC.Value,
StartTime: event.StartTime.UTC(),
Source: EventSource(event.Source),
Status: EventStatus(event.Status),
IsFeatured: event.IsFeatured,
IsMonitored: event.IsMonitored,
IsActive: event.IsActive,
WinningUpperLimit: event.WinningUpperLimit,
Score: event.Score.Value,
MatchMinute: event.MatchMinute.Value,
TimerStatus: event.TimerStatus.Value,
AddedTime: event.AddedTime.Value,
MatchPeriod: event.MatchPeriod.Value,
IsLive: event.IsLive,
FetchedAt: event.FetchedAt.UTC(),
}
}
func ConvertEventWithSettingResList(events []EventWithSettings) []EventWithSettingsRes {
result := make([]EventWithSettingsRes, 0, len(events))
for _, event := range events {
result = append(result, ConvertEventWitSettingRes(event))
}
return result
}

View File

@ -0,0 +1,35 @@
package domain
type MatchResult struct {
EventID string
FullScore string
HalfScore string
Status string
Scores map[string]map[string]string
}
type B365UpcomingRes struct {
Success int `json:"success"`
Pager struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
}
Results []struct {
ID string `json:"id"`
SportID string `json:"sport_id"`
Time string `json:"time"`
League struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"league"`
Home struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"home"`
Away *struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"away"`
} `json:"results"`
}

View File

@ -0,0 +1,63 @@
package domain
// These leagues are automatically featured when the league is created
var FeaturedLeagues = []int64{
// Football
10044469, // Ethiopian Premier League
10041282, //Premier League
10083364, //La Liga
10041095, //German Bundesliga
10041100, //Ligue 1
10041809, //UEFA Champions League
10041957, //UEFA Europa League
10079560, //UEFA Conference League
10050282, //UEFA Nations League
10044685, //FIFA Club World Cup
10050346, //UEFA Super Cup
10081269, //CONCACAF Champions Cup
10070189, //CONCACAF Gold Cup
10076185, //UEFA Regions Cup
10067913, //Europe - World Cup Qualifying
10040162, //Asia - World Cup Qualifying
10067624, //South America - World Cup Qualifying
10073057, //North & Central America - World Cup Qualifying
10037075, //International Match
10077480, //Womens International
10037109, //Europe Friendlies
10068837, //Euro U21
10041315, //Italian Serie A
10036538, //Spain Segunda
10047168, // US MLS
10043156, //England FA Cup
10042103, //France Cup
10041088, //Premier League 2
10084250, //Turkiye Super League
10041187, //Kenya Super League
10041391, //Netherlands Eredivisie
// Basketball
10041830, //NBA
10049984, //WNBA
10037165, //German Bundesliga
10036608, //Italian Lega 1
10040795, //EuroLeague
10041534, //Basketball Africa League
// Ice Hockey
10037477, //NHL
10037447, //AHL
10069385, //IIHF World Championship
// AMERICAN FOOTBALL
10037219, //NFL
// BASEBALL
10037485, // MLB
// VOLLEYBALL
10069666, //FIVB Nations League
}

View File

@ -1,6 +1,24 @@
package domain package domain
type League struct { import (
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
)
// The ID and the Bet365 IDs we have here are both gotten from the betapi
type LeagueWithSettings struct {
ID int64
Name string
CompanyID int64
CountryCode ValidString
Bet365ID ValidInt32
SportID int32
IsActive bool
IsFeatured bool
UpdatedAt time.Time
}
type LeagueWithSettingsRes struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
Name string `json:"name" example:"BPL"` Name string `json:"name" example:"BPL"`
CountryCode string `json:"cc" example:"uk"` CountryCode string `json:"cc" example:"uk"`
@ -9,6 +27,49 @@ type League struct {
SportID int32 `json:"sport_id" example:"1"` SportID int32 `json:"sport_id" example:"1"`
IsFeatured bool `json:"is_featured" example:"false"` IsFeatured bool `json:"is_featured" example:"false"`
} }
type BaseLeague struct {
ID int64
Name string
CountryCode ValidString
Bet365ID ValidInt32
SportID int32
DefaultIsActive bool
DefaultIsFeatured bool
}
type BaseLeagueRes struct {
ID int64 `json:"id" example:"1"`
Name string `json:"name" example:"BPL"`
CountryCode string `json:"cc" example:"uk"`
Bet365ID int32 `json:"bet365_id" example:"1121"`
SportID int32 `json:"sport_id" example:"1"`
DefaultIsActive bool `json:"default_is_active" example:"false"`
DefaultIsFeatured bool `json:"default_is_featured" example:"false"`
}
type CreateLeague struct {
ID int64
Name string
CountryCode ValidString
Bet365ID ValidInt32
SportID int32
DefaultIsActive bool
DefaultIsFeatured bool
}
type LeagueSettings struct {
CompanyID int64
LeagueID int64
IsActive ValidBool
IsFeatured ValidBool
UpdatedAt time.Time
}
type CreateLeagueSettings struct {
CompanyID int64
LeagueID int64
IsActive ValidBool
IsFeatured ValidBool
}
type UpdateLeague struct { type UpdateLeague struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
@ -29,64 +90,87 @@ type LeagueFilter struct {
Offset ValidInt64 Offset ValidInt64
} }
// These leagues are automatically featured when the league is created func ConvertCreateLeague(league CreateLeague) dbgen.InsertLeagueParams {
var FeaturedLeagues = []int64{ return dbgen.InsertLeagueParams{
// Football ID: league.ID,
10044469, // Ethiopian Premier League Name: league.Name,
10041282, //Premier League CountryCode: league.CountryCode.ToPG(),
10083364, //La Liga Bet365ID: league.Bet365ID.ToPG(),
10041095, //German Bundesliga SportID: league.SportID,
10041100, //Ligue 1 DefaultIsActive: league.DefaultIsActive,
10041809, //UEFA Champions League DefaultIsFeatured: league.DefaultIsFeatured,
10041957, //UEFA Europa League }
10079560, //UEFA Conference League }
10050282, //UEFA Nations League
10044685, //FIFA Club World Cup func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.InsertLeagueSettingsParams {
10050346, //UEFA Super Cup return dbgen.InsertLeagueSettingsParams{
10081269, //CONCACAF Champions Cup CompanyID: leagueSetting.CompanyID,
10070189, //CONCACAF Gold Cup LeagueID: leagueSetting.LeagueID,
10076185, //UEFA Regions Cup IsActive: leagueSetting.IsActive.ToPG(),
IsFeatured: leagueSetting.IsFeatured.ToPG(),
10067913, //Europe - World Cup Qualifying }
10040162, //Asia - World Cup Qualifying }
10067624, //South America - World Cup Qualifying
10073057, //North & Central America - World Cup Qualifying func ConvertDBBaseLeague(league dbgen.League) BaseLeague {
return BaseLeague{
10037075, //International Match ID: league.ID,
10077480, //Womens International Name: league.Name,
10037109, //Europe Friendlies CountryCode: ValidString{
10068837, //Euro U21 Value: league.CountryCode.String,
Valid: league.CountryCode.Valid,
10041315, //Italian Serie A },
10036538, //Spain Segunda Bet365ID: ValidInt32{
10047168, // US MLS Value: league.Bet365ID.Int32,
Valid: league.Bet365ID.Valid,
10043156, //England FA Cup },
10042103, //France Cup SportID: league.SportID,
10041088, //Premier League 2 DefaultIsActive: league.DefaultIsActive,
10084250, //Turkiye Super League DefaultIsFeatured: league.DefaultIsFeatured,
10041187, //Kenya Super League }
10041391, //Netherlands Eredivisie }
// Basketball func ConvertDBBaseLeagues(leagues []dbgen.League) []BaseLeague {
10041830, //NBA result := make([]BaseLeague, len(leagues))
10049984, //WNBA for i, league := range leagues {
10037165, //German Bundesliga result[i] = ConvertDBBaseLeague(league)
10036608, //Italian Lega 1 }
10040795, //EuroLeague return result
10041534, //Basketball Africa League }
// Ice Hockey func ConvertDBLeagueWithSetting(lws dbgen.LeagueWithSetting) LeagueWithSettings {
10037477, //NHL return LeagueWithSettings{
10037447, //AHL ID: lws.ID,
10069385, //IIHF World Championship Name: lws.Name,
CompanyID: lws.CompanyID,
// AMERICAN FOOTBALL CountryCode: ValidString{
10037219, //NFL Value: lws.CountryCode.String,
Valid: lws.CountryCode.Valid,
// BASEBALL },
10037485, // MLB Bet365ID: ValidInt32{
Value: lws.Bet365ID.Int32,
// VOLLEYBALL Valid: lws.Bet365ID.Valid,
10069666, //FIVB Nations League },
IsActive: lws.IsActive,
SportID: lws.SportID,
IsFeatured: lws.IsFeatured,
UpdatedAt: lws.UpdatedAt.Time,
}
}
func ConvertDBLeagueWithSettings(lws []dbgen.LeagueWithSetting) []LeagueWithSettings {
result := make([]LeagueWithSettings, len(lws))
for i, league := range lws {
result[i] = ConvertDBLeagueWithSetting(league)
}
return result
}
func ConvertUpdateLeague(updateLeague UpdateLeague) dbgen.UpdateLeagueParams {
return dbgen.UpdateLeagueParams{
ID: updateLeague.ID,
Name: updateLeague.Name.ToPG(),
CountryCode: updateLeague.CountryCode.ToPG(),
Bet365ID: updateLeague.Bet365ID.ToPG(),
SportID: updateLeague.SportID.ToPG(),
}
} }

View File

@ -1,17 +0,0 @@
package domain
import (
"time"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
var BadRequestZapFields = []zap.Field{
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
}
var InternalServerErrorZapFields = []zap.Field{
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
}

View File

@ -3,47 +3,184 @@ package domain
import ( import (
"encoding/json" "encoding/json"
"time" "time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/jackc/pgx/v5/pgtype"
) )
type Market struct { type CreateOddMarket struct {
EventID string EventID string
FI string
MarketCategory string MarketCategory string
MarketType string MarketType string
MarketName string MarketName string
MarketID string MarketID string
UpdatedAt time.Time UpdatedAt time.Time
Odds []map[string]interface{} Odds []map[string]interface{}
Name string
Handicap string
OddsVal float64
Source string
} }
type Odd struct { type OddMarket struct {
ID int64 `json:"id"`
EventID string `json:"event_id"`
MarketType string `json:"market_type"`
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"`
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"` ID int64 `json:"id"`
EventID string `json:"event_id"` EventID string `json:"event_id"`
Fi string `json:"fi"`
MarketType string `json:"market_type"` MarketType string `json:"market_type"`
MarketName string `json:"market_name"` MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"` MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"` MarketID string `json:"market_id"`
Name string `json:"name"`
Handicap string `json:"handicap"`
OddsValue float64 `json:"odds_value"`
Section string `json:"section"`
Category string `json:"category"`
RawOdds []json.RawMessage `json:"raw_odds"` RawOdds []json.RawMessage `json:"raw_odds"`
FetchedAt time.Time `json:"fetched_at"` FetchedAt time.Time `json:"fetched_at"`
ExpiresAt time.Time `json:"expires_at"` ExpiresAt time.Time `json:"expires_at"`
Source string `json:"source"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
} }
type RawOddsByMarketID struct {
ID int64 `json:"id"` type OddMarketSettings struct {
MarketName string `json:"market_name"` CompanyID int64
Handicap string `json:"handicap"` OddMarketID int64
RawOdds []json.RawMessage `json:"raw_odds"` IsActive ValidBool
FetchedAt time.Time `json:"fetched_at"` CustomRawOdds string
ExpiresAt time.Time `json:"expires_at"` UpdatedAt time.Time
}
type CreateOddMarketSettings struct {
CompanyID int64
OddMarketID int64
IsActive ValidBool
CustomRawOdds []map[string]interface{}
}
// type RawOddsByMarketID struct {
// ID int64 `json:"id"`
// MarketName string `json:"market_name"`
// Handicap string `json:"handicap"`
// RawOdds []json.RawMessage `json:"raw_odds"`
// FetchedAt time.Time `json:"fetched_at"`
// ExpiresAt time.Time `json:"expires_at"`
// }
type OddMarketFilter struct {
Limit ValidInt32
Offset ValidInt32
}
type OddMarketWithEventFilter struct {
Limit ValidInt32
Offset ValidInt32
}
func ConvertDBOddMarket(oddMarket dbgen.OddsMarketWithEvent) (OddMarket, error) {
var rawOdds []json.RawMessage
if len(oddMarket.RawOdds) > 0 {
if err := json.Unmarshal(oddMarket.RawOdds, &rawOdds); err != nil {
return OddMarket{}, err
}
} else {
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),
}, nil
}
func ConvertDBOddMarkets(oddMarkets []dbgen.OddsMarketWithEvent) ([]OddMarket, error) {
result := make([]OddMarket, len(oddMarkets))
for i, om := range oddMarkets {
convertedMarket, err := ConvertDBOddMarket(om)
if err != nil {
return nil, err
}
result[i] = convertedMarket
}
return result, nil
}
func ConvertCreateOddMarket(oddMarket CreateOddMarket) (dbgen.InsertOddsMarketParams, error) {
rawOddsBytes, err := json.Marshal(oddMarket.Odds)
if err != nil {
return dbgen.InsertOddsMarketParams{}, err
}
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},
}, nil
}
func ConvertCreateOddMarketSetting(oms CreateOddMarketSettings) (dbgen.InsertOddSettingsParams, error) {
rawOddsBytes, err := json.Marshal(oms.CustomRawOdds)
if err != nil {
return dbgen.InsertOddSettingsParams{}, err
}
return dbgen.InsertOddSettingsParams{
CompanyID: oms.CompanyID,
OddsMarketID: oms.OddMarketID,
IsActive: oms.IsActive.ToPG(),
CustomRawOdds: rawOddsBytes,
}, nil
}
func ConvertDBOddMarketWithSetting(oms dbgen.OddsMarketWithSetting) (OddMarketWithSettings, error) {
var rawOdds []json.RawMessage
if len(oms.RawOdds) > 0 {
if err := json.Unmarshal(oms.RawOdds, &rawOdds); err != nil {
return OddMarketWithSettings{}, err
}
} else {
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,
}, nil
}
func ConvertDBOddMarketWithSettings(oms []dbgen.OddsMarketWithSetting) ([]OddMarketWithSettings, error) {
result := make([]OddMarketWithSettings, len(oms))
for i, o := range oms {
converted, err := ConvertDBOddMarketWithSetting(o)
if err != nil {
return nil, err
}
result[i] = converted
}
return result, nil
} }

View File

@ -35,7 +35,7 @@ type OddHistoryFilter struct {
func ConvertCreateOddHistory(odd CreateOddHistory) dbgen.InsertOddHistoryParams { func ConvertCreateOddHistory(odd CreateOddHistory) dbgen.InsertOddHistoryParams {
return dbgen.InsertOddHistoryParams{ return dbgen.InsertOddHistoryParams{
OddID: odd.OddID, OddsMarketID: odd.OddID,
MarketID: odd.MarketID, MarketID: odd.MarketID,
RawOddID: odd.RawOddID, RawOddID: odd.RawOddID,
EventID: odd.EventID, EventID: odd.EventID,
@ -46,7 +46,7 @@ func ConvertCreateOddHistory(odd CreateOddHistory) dbgen.InsertOddHistoryParams
func ConvertDBOddHistory(dbOddHistory dbgen.OddHistory) OddHistory { func ConvertDBOddHistory(dbOddHistory dbgen.OddHistory) OddHistory {
return OddHistory{ return OddHistory{
ID: dbOddHistory.ID, ID: dbOddHistory.ID,
OddID: dbOddHistory.OddID, OddID: dbOddHistory.OddsMarketID,
MarketID: dbOddHistory.MarketID, MarketID: dbOddHistory.MarketID,
RawOddID: dbOddHistory.RawOddID, RawOddID: dbOddHistory.RawOddID,
EventID: dbOddHistory.EventID, EventID: dbOddHistory.EventID,

View File

@ -9,20 +9,20 @@ type BaseResultResponse struct {
Results []json.RawMessage `json:"results"` Results []json.RawMessage `json:"results"`
} }
type LeagueRes struct { type LeagueResultResponse struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
CC string `json:"cc"` CC string `json:"cc"`
} }
type Team struct { type TeamResultResponse struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
ImageID ValidInt64 `json:"image_id"` ImageID ValidInt64 `json:"image_id"`
CC string `json:"cc"` CC string `json:"cc"`
} }
type Score struct { type ScoreResultResponse struct {
Home string `json:"home"` Home string `json:"home"`
Away string `json:"away"` Away string `json:"away"`
} }
@ -32,9 +32,9 @@ type CommonResultResponse struct {
SportID string `json:"sport_id"` SportID string `json:"sport_id"`
Time string `json:"time"` Time string `json:"time"`
TimeStatus ValidInt64 `json:"time_status"` TimeStatus ValidInt64 `json:"time_status"`
League LeagueRes `json:"league"` League LeagueResultResponse `json:"league"`
Home Team `json:"home"` Home TeamResultResponse `json:"home"`
Away Team `json:"away"` Away TeamResultResponse `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
} }
@ -43,13 +43,13 @@ type FootballResultResponse struct {
SportID string `json:"sport_id"` SportID string `json:"sport_id"`
Time string `json:"time"` Time string `json:"time"`
TimeStatus string `json:"time_status"` TimeStatus string `json:"time_status"`
League LeagueRes `json:"league"` League LeagueResultResponse `json:"league"`
Home Team `json:"home"` Home TeamResultResponse `json:"home"`
Away Team `json:"away"` Away TeamResultResponse `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
Scores struct { Scores struct {
FirstHalf Score `json:"1"` FirstHalf ScoreResultResponse `json:"1"`
SecondHalf Score `json:"2"` SecondHalf ScoreResultResponse `json:"2"`
} `json:"scores"` } `json:"scores"`
Stats struct { Stats struct {
Attacks []string `json:"attacks"` Attacks []string `json:"attacks"`
@ -82,17 +82,17 @@ type BasketballResultResponse struct {
SportID string `json:"sport_id"` SportID string `json:"sport_id"`
Time string `json:"time"` Time string `json:"time"`
TimeStatus string `json:"time_status"` TimeStatus string `json:"time_status"`
League LeagueRes `json:"league"` League LeagueResultResponse `json:"league"`
Home Team `json:"home"` Home TeamResultResponse `json:"home"`
Away Team `json:"away"` Away TeamResultResponse `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
Scores struct { Scores struct {
FirstQuarter Score `json:"1"` FirstQuarter ScoreResultResponse `json:"1"`
SecondQuarter Score `json:"2"` SecondQuarter ScoreResultResponse `json:"2"`
FirstHalf Score `json:"3"` FirstHalf ScoreResultResponse `json:"3"`
ThirdQuarter Score `json:"4"` ThirdQuarter ScoreResultResponse `json:"4"`
FourthQuarter Score `json:"5"` FourthQuarter ScoreResultResponse `json:"5"`
TotalScore Score `json:"7"` TotalScore ScoreResultResponse `json:"7"`
} `json:"scores"` } `json:"scores"`
Stats struct { Stats struct {
TwoPoints []string `json:"2points"` TwoPoints []string `json:"2points"`
@ -129,15 +129,15 @@ type IceHockeyResultResponse struct {
SportID string `json:"sport_id"` SportID string `json:"sport_id"`
Time string `json:"time"` Time string `json:"time"`
TimeStatus string `json:"time_status"` TimeStatus string `json:"time_status"`
League LeagueRes `json:"league"` League LeagueResultResponse `json:"league"`
Home Team `json:"home"` Home TeamResultResponse `json:"home"`
Away Team `json:"away"` Away TeamResultResponse `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
Scores struct { Scores struct {
FirstPeriod Score `json:"1"` FirstPeriod ScoreResultResponse `json:"1"`
SecondPeriod Score `json:"2"` SecondPeriod ScoreResultResponse `json:"2"`
ThirdPeriod Score `json:"3"` ThirdPeriod ScoreResultResponse `json:"3"`
TotalScore Score `json:"5"` TotalScore ScoreResultResponse `json:"5"`
} `json:"scores"` } `json:"scores"`
Stats struct { Stats struct {
@ -222,11 +222,11 @@ type VolleyballResultResponse struct {
} `json:"away"` } `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
Scores struct { Scores struct {
FirstSet Score `json:"1"` FirstSet ScoreResultResponse `json:"1"`
SecondSet Score `json:"2"` SecondSet ScoreResultResponse `json:"2"`
ThirdSet Score `json:"3"` ThirdSet ScoreResultResponse `json:"3"`
FourthSet Score `json:"4"` FourthSet ScoreResultResponse `json:"4"`
FivethSet Score `json:"5"` FivethSet ScoreResultResponse `json:"5"`
} `json:"scores"` } `json:"scores"`
Stats struct { Stats struct {
PointsWonOnServe []string `json:"points_won_on_serve"` PointsWonOnServe []string `json:"points_won_on_serve"`
@ -290,10 +290,10 @@ type FutsalResultResponse struct {
} `json:"away"` } `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
Scores struct { Scores struct {
FirstPeriod Score `json:"1"` FirstPeriod ScoreResultResponse `json:"1"`
SecondPeriod Score `json:"2"` SecondPeriod ScoreResultResponse `json:"2"`
ThirdPeriod Score `json:"3"` ThirdPeriod ScoreResultResponse `json:"3"`
TotalScore Score `json:"4"` TotalScore ScoreResultResponse `json:"4"`
} `json:"scores"` } `json:"scores"`
Events []map[string]string `json:"events"` Events []map[string]string `json:"events"`
InplayCreatedAt string `json:"inplay_created_at"` InplayCreatedAt string `json:"inplay_created_at"`
@ -327,12 +327,12 @@ type NFLResultResponse struct {
} `json:"away"` } `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
Scores struct { Scores struct {
FirstQuarter Score `json:"1"` FirstQuarter ScoreResultResponse `json:"1"`
SecondQuarter Score `json:"2"` SecondQuarter ScoreResultResponse `json:"2"`
ThirdQuarter Score `json:"3"` ThirdQuarter ScoreResultResponse `json:"3"`
FourthQuarter Score `json:"4"` FourthQuarter ScoreResultResponse `json:"4"`
Overtime Score `json:"5"` Overtime ScoreResultResponse `json:"5"`
TotalScore Score `json:"7"` TotalScore ScoreResultResponse `json:"7"`
} `json:"scores"` } `json:"scores"`
Stats struct { Stats struct {
FirstDowns []string `json:"first_downs"` FirstDowns []string `json:"first_downs"`
@ -381,9 +381,9 @@ type RugbyResultResponse struct {
} `json:"away"` } `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
Scores struct { Scores struct {
FirstHalf Score `json:"1"` FirstHalf ScoreResultResponse `json:"1"`
SecondHalf Score `json:"2"` SecondHalf ScoreResultResponse `json:"2"`
TotalScore Score `json:"7"` TotalScore ScoreResultResponse `json:"7"`
} `json:"scores"` } `json:"scores"`
Stats struct { Stats struct {
Tries []string `json:"tries"` Tries []string `json:"tries"`
@ -433,17 +433,17 @@ type BaseballResultResponse struct {
} `json:"away"` } `json:"away"`
SS string `json:"ss"` SS string `json:"ss"`
Scores struct { Scores struct {
FirstInning Score `json:"1"` FirstInning ScoreResultResponse `json:"1"`
SecondInning Score `json:"2"` SecondInning ScoreResultResponse `json:"2"`
ThirdInning Score `json:"3"` ThirdInning ScoreResultResponse `json:"3"`
FourthInning Score `json:"4"` FourthInning ScoreResultResponse `json:"4"`
FifthInning Score `json:"5"` FifthInning ScoreResultResponse `json:"5"`
SixthInning Score `json:"6"` SixthInning ScoreResultResponse `json:"6"`
SeventhInning Score `json:"7"` SeventhInning ScoreResultResponse `json:"7"`
EighthInning Score `json:"8"` EighthInning ScoreResultResponse `json:"8"`
NinthInning Score `json:"9"` NinthInning ScoreResultResponse `json:"9"`
ExtraInnings Score `json:"10"` ExtraInnings ScoreResultResponse `json:"10"`
TotalScore Score `json:"11"` TotalScore ScoreResultResponse `json:"11"`
} `json:"scores"` } `json:"scores"`
Stats struct { Stats struct {
Hits []string `json:"hits"` Hits []string `json:"hits"`

View File

@ -17,17 +17,13 @@ type User struct {
PhoneNumber string `json:"phone_number"` PhoneNumber string `json:"phone_number"`
Password []byte Password []byte
Role Role Role Role
//
EmailVerified bool EmailVerified bool
PhoneVerified bool PhoneVerified bool
//
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
//
SuspendedAt time.Time SuspendedAt time.Time
Suspended bool Suspended bool
// CompanyID ValidInt64 //This should be null
CompanyID ValidInt64
} }
type UserFilter struct { type UserFilter struct {
@ -54,6 +50,7 @@ type RegisterUserReq struct {
Otp string Otp string
ReferralCode string `json:"referral_code"` ReferralCode string `json:"referral_code"`
OtpMedium OtpMedium OtpMedium OtpMedium
CompanyID ValidInt64
} }
type CreateUserReq struct { type CreateUserReq struct {
FirstName string FirstName string

View File

@ -115,6 +115,15 @@ func ConvertInt32Ptr(value *int32) ValidInt32 {
Valid: true, Valid: true,
} }
} }
func ConvertIntPtr(value *int) ValidInt {
if value == nil {
return ValidInt{}
}
return ValidInt{
Value: *value,
Valid: true,
}
}
func ConvertStringPtr(value *string) ValidString { func ConvertStringPtr(value *string) ValidString {
if value == nil { if value == nil {

View File

@ -0,0 +1,42 @@
package helpers
import (
"regexp"
"strings"
"unicode"
)
var (
// keep letters, numbers, spaces, and hyphens
slugRegex = regexp.MustCompile(`[^a-z0-9\s-]`)
spaceRe = regexp.MustCompile(`\s+`)
dashRe = regexp.MustCompile(`-+`)
)
// GenerateSlug creates a URL-safe slug from a given string (e.g. company name).
func GenerateSlug(name string) string {
// lowercase
slug := strings.ToLower(name)
// normalize unicode accents (é -> e, ü -> u, etc.)
slug = strings.Map(func(r rune) rune {
if unicode.IsLetter(r) || unicode.IsDigit(r) || unicode.IsSpace(r) || r == '-' {
return r
}
return -1
}, slug)
// remove unwanted chars
slug = slugRegex.ReplaceAllString(slug, "")
// replace spaces with dash
slug = spaceRe.ReplaceAllString(slug, "-")
// collapse multiple dashes
slug = dashRe.ReplaceAllString(slug, "-")
// trim leading/trailing dash
slug = strings.Trim(slug, "-")
return slug
}

View File

@ -11,7 +11,7 @@ import (
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string) (domain.User, error) { func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error) {
user, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{ user, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{
Email: pgtype.Text{ Email: pgtype.Text{
String: email, String: email,
@ -21,6 +21,7 @@ func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string) (d
String: phone, String: phone,
Valid: true, Valid: true,
}, },
CompanyID: companyID.ToPG(),
}) })
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {

View File

@ -80,7 +80,7 @@ func convertDBFlag(flag dbgen.Flag) domain.Flag {
return domain.Flag{ return domain.Flag{
ID: flag.ID, ID: flag.ID,
BetID: flag.BetID.Int64, BetID: flag.BetID.Int64,
OddID: flag.OddID.Int64, OddID: flag.OddsMarketID.Int64,
Reason: flag.Reason.String, Reason: flag.Reason.String,
FlaggedAt: flag.FlaggedAt.Time, FlaggedAt: flag.FlaggedAt.Time,
Resolved: flag.Resolved.Bool, Resolved: flag.Resolved.Bool,
@ -157,7 +157,7 @@ func (s *Store) CreateFlag(ctx context.Context, flag domain.CreateFlagReq) (doma
Int64: flag.BetID, Int64: flag.BetID,
Valid: flag.BetID != 0, Valid: flag.BetID != 0,
}, },
OddID: pgtype.Int8{ OddsMarketID: pgtype.Int8{
Int64: flag.OddID, Int64: flag.OddID,
Valid: flag.OddID != 0, Valid: flag.OddID != 0,
}, },

View File

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

View File

@ -1,5 +1,105 @@
package repository package repository
import "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" // import (
// "context"
func (s *Store) InsertCustomOdds(ctx context.Context, odd domain.ConvertCreateCustomOdd) (domain.) // dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
// )
// func (s *Store) InsertCustomOdds(ctx context.Context, odd domain.CreateCustomOdd) (domain.CustomOdd, error) {
// convertedCustomOdd, err := domain.ConvertCreateCustomOdd(odd)
// if err != nil {
// return domain.CustomOdd{}, err
// }
// dbCustomOdd, err := s.queries.InsertCustomOdd(ctx, convertedCustomOdd)
// if err != nil {
// return domain.CustomOdd{}, err
// }
// convertDbCustomOdd, err := domain.ConvertDBCustomOdd(dbCustomOdd)
// if err != nil {
// return domain.CustomOdd{}, err
// }
// return convertDbCustomOdd, nil
// }
// func (s *Store) GetAllCustomOdds(ctx context.Context, filter domain.CustomOddFilter) ([]domain.CustomOdd, error) {
// dbCustomOdds, err := s.queries.GetAllCustomOdds(ctx, filter.CompanyID.ToPG())
// if err != nil {
// return nil, err
// }
// convertDbCustomOdds, err := domain.ConvertDbCustomOdds(dbCustomOdds)
// if err != nil {
// return nil, err
// }
// return convertDbCustomOdds, nil
// }
// func (s *Store) GetCustomOddByID(ctx context.Context, id int64) (domain.CustomOdd, error) {
// dbCustomOdd, err := s.queries.GetCustomOddByID(ctx, id)
// if err != nil {
// return domain.CustomOdd{}, nil
// }
// convertedDBCustomOdd, err := domain.ConvertDBCustomOdd(dbCustomOdd)
// if err != nil {
// return domain.CustomOdd{}, nil
// }
// return convertedDBCustomOdd, nil
// }
// func (s *Store) GetCustomOddByOddID(ctx context.Context, oddId int64, companyID int64) (domain.CustomOdd, error) {
// dbCustomOdd, err := s.queries.GetCustomOddByOddID(ctx, dbgen.GetCustomOddByOddIDParams{
// OddID: oddId,
// CompanyID: companyID,
// })
// if err != nil {
// return domain.CustomOdd{}, nil
// }
// convertedDBCustomOdd, err := domain.ConvertDBCustomOdd(dbCustomOdd)
// if err != nil {
// return domain.CustomOdd{}, nil
// }
// return convertedDBCustomOdd, nil
// }
// func (s *Store) DeleteCustomOddByID(ctx context.Context, id int64) error {
// err := s.queries.DeleteCustomOddsByID(ctx, id)
// if err != nil {
// return err
// }
// return nil
// }
// func (s *Store) DeleteCustomOddsByOddID(ctx context.Context, oddId int64, companyID int64) error {
// err := s.queries.DeleteCustomOddsByOddID(ctx, dbgen.DeleteCustomOddsByOddIDParams{
// OddID: oddId,
// CompanyID: companyID,
// })
// if err != nil {
// return err
// }
// return nil
// }
// func (s *Store) DeleteCustomOddByEventID(ctx context.Context, eventID string) error {
// err := s.queries.DeleteCustomOddByEventID(ctx, eventID)
// if err != nil {
// return err
// }
// return nil
// }

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"math" "math"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -13,92 +12,29 @@ import (
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
func (s *Store) SaveEvent(ctx context.Context, e domain.Event) error { func (s *Store) SaveEvent(ctx context.Context, e domain.CreateEvent) error {
parsedTime, err := time.Parse(time.RFC3339, e.StartTime) return s.queries.InsertEvent(ctx, domain.ConvertCreateEvent(e))
if err != nil {
return err
}
return s.queries.InsertEvent(ctx, dbgen.InsertEventParams{
ID: e.ID,
SportID: pgtype.Int4{Int32: e.SportID, Valid: true},
MatchName: pgtype.Text{String: e.MatchName, Valid: true},
HomeTeam: pgtype.Text{String: e.HomeTeam, Valid: true},
AwayTeam: pgtype.Text{String: e.AwayTeam, Valid: true},
HomeTeamID: pgtype.Int4{Int32: e.HomeTeamID, Valid: true},
AwayTeamID: pgtype.Int4{Int32: e.AwayTeamID, Valid: true},
HomeKitImage: pgtype.Text{String: e.HomeKitImage, Valid: true},
AwayKitImage: pgtype.Text{String: e.AwayKitImage, Valid: true},
LeagueID: pgtype.Int4{Int32: e.LeagueID, Valid: true},
LeagueName: pgtype.Text{String: e.LeagueName, Valid: true},
LeagueCc: pgtype.Text{String: e.LeagueCC, Valid: true},
StartTime: pgtype.Timestamp{Time: parsedTime, Valid: true},
Score: pgtype.Text{String: e.Score, Valid: true},
MatchMinute: pgtype.Int4{Int32: int32(e.MatchMinute), Valid: true},
TimerStatus: pgtype.Text{String: e.TimerStatus, Valid: true},
AddedTime: pgtype.Int4{Int32: int32(e.AddedTime), Valid: true},
MatchPeriod: pgtype.Int4{Int32: int32(e.MatchPeriod), Valid: true},
IsLive: pgtype.Bool{Bool: e.IsLive, Valid: true},
Status: pgtype.Text{String: e.Status, Valid: true},
Source: pgtype.Text{String: e.Source, Valid: true},
})
} }
func (s *Store) SaveUpcomingEvent(ctx context.Context, e domain.UpcomingEvent) error {
return s.queries.InsertUpcomingEvent(ctx, dbgen.InsertUpcomingEventParams{ func (s *Store) InsertEventSettings(ctx context.Context, eventSetting domain.CreateEventSettings) error {
ID: e.ID, return s.queries.InsertEventSettings(ctx, domain.ConvertCreateEventSettings(eventSetting))
SportID: pgtype.Int4{Int32: e.SportID, Valid: true},
MatchName: pgtype.Text{String: e.MatchName, Valid: true},
HomeTeam: pgtype.Text{String: e.HomeTeam, Valid: true},
AwayTeam: pgtype.Text{String: e.AwayTeam, Valid: true},
HomeTeamID: pgtype.Int4{Int32: e.HomeTeamID, Valid: true},
AwayTeamID: pgtype.Int4{Int32: e.AwayTeamID, Valid: true},
HomeKitImage: pgtype.Text{String: e.HomeKitImage, Valid: true},
AwayKitImage: pgtype.Text{String: e.AwayKitImage, Valid: true},
LeagueID: pgtype.Int4{Int32: e.LeagueID, Valid: true},
LeagueName: pgtype.Text{String: e.LeagueName, Valid: true},
LeagueCc: pgtype.Text{String: e.LeagueCC, Valid: true},
StartTime: pgtype.Timestamp{Time: e.StartTime, Valid: true},
Source: pgtype.Text{String: e.Source, Valid: true},
})
} }
func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) { func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) {
return s.queries.ListLiveEvents(ctx) return s.queries.ListLiveEvents(ctx)
} }
func (s *Store) GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) {
func (s *Store) GetAllUpcomingEvents(ctx context.Context) ([]domain.BaseEvent, error) {
events, err := s.queries.GetAllUpcomingEvents(ctx) events, err := s.queries.GetAllUpcomingEvents(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
upcomingEvents := make([]domain.UpcomingEvent, len(events)) return domain.ConvertDBEvents(events), nil
for i, e := range events {
upcomingEvents[i] = domain.UpcomingEvent{
ID: e.ID,
SportID: e.SportID.Int32,
MatchName: e.MatchName.String,
HomeTeam: e.HomeTeam.String,
AwayTeam: e.AwayTeam.String,
HomeTeamID: e.HomeTeamID.Int32,
AwayTeamID: e.AwayTeamID.Int32,
HomeKitImage: e.HomeKitImage.String,
AwayKitImage: e.AwayKitImage.String,
LeagueID: e.LeagueID.Int32,
LeagueName: e.LeagueName.String,
LeagueCC: e.LeagueCc.String,
StartTime: e.StartTime.Time.UTC(),
Source: e.Source.String,
Status: domain.EventStatus(e.Status.String),
IsFeatured: e.IsFeatured,
IsMonitored: e.IsMonitored,
IsActive: e.IsActive,
}
}
return upcomingEvents, nil
} }
func (s *Store) GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, error) { func (s *Store) GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, error) {
events, err := s.queries.GetExpiredUpcomingEvents(ctx, pgtype.Text{ events, err := s.queries.GetExpiredEvents(ctx, pgtype.Text{
String: filter.MatchStatus.Value, String: filter.MatchStatus.Value,
Valid: filter.MatchStatus.Valid, Valid: filter.MatchStatus.Valid,
}) })
@ -106,166 +42,98 @@ func (s *Store) GetExpiredUpcomingEvents(ctx context.Context, filter domain.Even
return nil, err return nil, err
} }
upcomingEvents := make([]domain.UpcomingEvent, len(events)) return domain.ConvertDBEvents(events), nil
for i, e := range events {
upcomingEvents[i] = domain.UpcomingEvent{
ID: e.ID,
SportID: e.SportID.Int32,
MatchName: e.MatchName.String,
HomeTeam: e.HomeTeam.String,
AwayTeam: e.AwayTeam.String,
HomeTeamID: e.HomeTeamID.Int32,
AwayTeamID: e.AwayTeamID.Int32,
HomeKitImage: e.HomeKitImage.String,
AwayKitImage: e.AwayKitImage.String,
LeagueID: e.LeagueID.Int32,
LeagueName: e.LeagueName.String,
LeagueCC: e.LeagueCc.String,
StartTime: e.StartTime.Time.UTC(),
Source: e.Source.String,
Status: domain.EventStatus(e.Status.String),
IsFeatured: e.IsFeatured,
IsActive: e.IsActive,
}
}
return upcomingEvents, nil
} }
func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, int64, error) { func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) {
events, err := s.queries.GetPaginatedUpcomingEvents(ctx, dbgen.GetPaginatedUpcomingEventsParams{ events, err := s.queries.GetPaginatedUpcomingEvents(ctx, dbgen.GetPaginatedUpcomingEventsParams{
LeagueID: pgtype.Int4{ LeagueID: filter.LeagueID.ToPG(),
Int32: int32(filter.LeagueID.Value), SportID: filter.SportID.ToPG(),
Valid: filter.LeagueID.Valid, Query: filter.Query.ToPG(),
}, Limit: filter.Limit.ToPG(),
SportID: pgtype.Int4{ Offset: filter.Offset.ToPG(),
Int32: int32(filter.SportID.Value), FirstStartTime: filter.FirstStartTime.ToPG(),
Valid: filter.SportID.Valid, LastStartTime: filter.LastStartTime.ToPG(),
}, CountryCode: filter.CountryCode.ToPG(),
Query: pgtype.Text{
String: filter.Query.Value,
Valid: filter.Query.Valid,
},
Limit: pgtype.Int4{
Int32: int32(filter.Limit.Value),
Valid: filter.Limit.Valid,
},
Offset: pgtype.Int4{
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
FirstStartTime: pgtype.Timestamp{
Time: filter.FirstStartTime.Value.UTC(),
Valid: filter.FirstStartTime.Valid,
},
LastStartTime: pgtype.Timestamp{
Time: filter.LastStartTime.Value.UTC(),
Valid: filter.LastStartTime.Valid,
},
CountryCode: pgtype.Text{
String: filter.CountryCode.Value,
Valid: filter.CountryCode.Valid,
},
IsFeatured: pgtype.Bool{
Bool: filter.Featured.Valid,
Valid: filter.Featured.Valid,
},
}) })
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
upcomingEvents := make([]domain.UpcomingEvent, len(events))
for i, e := range events {
upcomingEvents[i] = domain.UpcomingEvent{
ID: e.ID,
SportID: e.SportID.Int32,
MatchName: e.MatchName.String,
HomeTeam: e.HomeTeam.String,
AwayTeam: e.AwayTeam.String,
HomeTeamID: e.HomeTeamID.Int32,
AwayTeamID: e.AwayTeamID.Int32,
HomeKitImage: e.HomeKitImage.String,
AwayKitImage: e.AwayKitImage.String,
LeagueID: e.LeagueID.Int32,
LeagueName: e.LeagueName.String,
LeagueCC: e.LeagueCc.String,
StartTime: e.StartTime.Time.UTC(),
Source: e.Source.String,
Status: domain.EventStatus(e.Status.String),
IsFeatured: e.IsFeatured,
IsActive: e.IsActive,
IsMonitored: e.IsMonitored,
}
}
totalCount, err := s.queries.GetTotalEvents(ctx, dbgen.GetTotalEventsParams{ totalCount, err := s.queries.GetTotalEvents(ctx, dbgen.GetTotalEventsParams{
LeagueID: pgtype.Int4{ LeagueID: filter.LeagueID.ToPG(),
Int32: int32(filter.LeagueID.Value), SportID: filter.SportID.ToPG(),
Valid: filter.LeagueID.Valid, Query: filter.Query.ToPG(),
}, FirstStartTime: filter.FirstStartTime.ToPG(),
SportID: pgtype.Int4{ LastStartTime: filter.LastStartTime.ToPG(),
Int32: int32(filter.SportID.Value), CountryCode: filter.CountryCode.ToPG(),
Valid: filter.SportID.Valid,
},
Query: pgtype.Text{
String: filter.Query.Value,
Valid: filter.Query.Valid,
},
FirstStartTime: pgtype.Timestamp{
Time: filter.FirstStartTime.Value.UTC(),
Valid: filter.FirstStartTime.Valid,
},
LastStartTime: pgtype.Timestamp{
Time: filter.LastStartTime.Value.UTC(),
Valid: filter.LastStartTime.Valid,
},
CountryCode: pgtype.Text{
String: filter.CountryCode.Value,
Valid: filter.CountryCode.Valid,
},
IsFeatured: pgtype.Bool{
Bool: filter.Featured.Valid,
Valid: filter.Featured.Valid,
},
}) })
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value)) numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value))
return upcomingEvents, int64(numberOfPages), nil return domain.ConvertDBEvents(events), int64(numberOfPages), nil
} }
func (s *Store) GetUpcomingEventByID(ctx context.Context, ID string) (domain.UpcomingEvent, error) {
event, err := s.queries.GetUpcomingByID(ctx, ID) 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(),
FirstStartTime: filter.FirstStartTime.ToPG(),
LastStartTime: filter.LastStartTime.ToPG(),
CountryCode: filter.CountryCode.ToPG(),
})
if err != nil { if err != nil {
return domain.UpcomingEvent{}, err return nil, 0, err
} }
return domain.UpcomingEvent{ totalCount, err := s.queries.GetTotalCompanyEvents(ctx, dbgen.GetTotalCompanyEventsParams{
ID: event.ID, CompanyID: companyID,
SportID: event.SportID.Int32, LeagueID: filter.LeagueID.ToPG(),
MatchName: event.MatchName.String, SportID: filter.SportID.ToPG(),
HomeTeam: event.HomeTeam.String, Query: filter.Query.ToPG(),
AwayTeam: event.AwayTeam.String, FirstStartTime: filter.FirstStartTime.ToPG(),
HomeTeamID: event.HomeTeamID.Int32, LastStartTime: filter.LastStartTime.ToPG(),
AwayTeamID: event.AwayTeamID.Int32, CountryCode: filter.CountryCode.ToPG(),
HomeKitImage: event.HomeKitImage.String, })
AwayKitImage: event.AwayKitImage.String, if err != nil {
LeagueID: event.LeagueID.Int32, return nil, 0, err
LeagueName: event.LeagueName.String, }
LeagueCC: event.LeagueCc.String,
StartTime: event.StartTime.Time.UTC(), numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value))
Source: event.Source.String, return domain.ConvertDBEventWithSettings(events), int64(numberOfPages), nil
Status: domain.EventStatus(event.Status.String), }
IsFeatured: event.IsFeatured, func (s *Store) GetUpcomingEventByID(ctx context.Context, ID string) (domain.BaseEvent, error) {
IsActive: event.IsActive, event, err := s.queries.GetUpcomingByID(ctx, ID)
IsMonitored: event.IsMonitored, if err != nil {
}, nil return domain.BaseEvent{}, err
}
return domain.ConvertDBEvent(event), nil
}
func (s *Store) GetEventWithSettingByID(ctx context.Context, ID string, companyID int64) (domain.EventWithSettings, error) {
event, err := s.queries.GetEventWithSettingByID(ctx, dbgen.GetEventWithSettingByIDParams{
ID: ID,
CompanyID: companyID,
})
if err != nil {
return domain.EventWithSettings{}, err
}
return domain.ConvertDBEventWithSetting(event), nil
} }
func (s *Store) UpdateFinalScore(ctx context.Context, eventID, fullScore string, status domain.EventStatus) error { func (s *Store) UpdateFinalScore(ctx context.Context, eventID, fullScore string, status domain.EventStatus) error {
params := dbgen.UpdateMatchResultParams{ params := dbgen.UpdateMatchResultParams{
Score: pgtype.Text{String: fullScore, Valid: true}, Score: pgtype.Text{String: fullScore, Valid: true},
Status: pgtype.Text{String: string(status), Valid: true}, Status: string(status),
ID: eventID, ID: eventID,
} }
@ -279,10 +147,7 @@ func (s *Store) UpdateFinalScore(ctx context.Context, eventID, fullScore string,
func (s *Store) UpdateEventStatus(ctx context.Context, eventID string, status domain.EventStatus) error { func (s *Store) UpdateEventStatus(ctx context.Context, eventID string, status domain.EventStatus) error {
params := dbgen.UpdateMatchResultParams{ params := dbgen.UpdateMatchResultParams{
Status: pgtype.Text{ Status: string(status),
String: string(status),
Valid: true,
},
ID: eventID, ID: eventID,
} }
@ -295,13 +160,6 @@ func (s *Store) UpdateEventStatus(ctx context.Context, eventID string, status do
} }
func (s *Store) UpdateEventFeatured(ctx context.Context, eventID string, isFeatured bool) error {
return s.queries.UpdateEventFeatured(ctx, dbgen.UpdateEventFeaturedParams{
ID: eventID,
IsFeatured: isFeatured,
})
}
func (s *Store) IsEventMonitored(ctx context.Context, eventID string) (bool, error) { func (s *Store) IsEventMonitored(ctx context.Context, eventID string) (bool, error) {
isMonitored, err := s.queries.IsEventMonitored(ctx, eventID) isMonitored, err := s.queries.IsEventMonitored(ctx, eventID)
@ -317,6 +175,10 @@ func (s *Store) UpdateEventMonitored(ctx context.Context, eventID string, IsMoni
}) })
} }
func (s *Store) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error {
return s.queries.UpdateEventSettings(ctx, domain.ConvertUpdateEventSettings(event))
}
func (s *Store) DeleteEvent(ctx context.Context, eventID string) error { func (s *Store) DeleteEvent(ctx context.Context, eventID string) error {
err := s.queries.DeleteEvent(ctx, eventID) err := s.queries.DeleteEvent(ctx, eventID)
if err != nil { if err != nil {

View File

@ -8,36 +8,18 @@ import (
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
func (s *Store) SaveLeague(ctx context.Context, l domain.League) error { func (s *Store) SaveLeague(ctx context.Context, league domain.CreateLeague) error {
return s.queries.InsertLeague(ctx, dbgen.InsertLeagueParams{ return s.queries.InsertLeague(ctx, domain.ConvertCreateLeague(league))
ID: l.ID,
Name: l.Name,
CountryCode: pgtype.Text{String: l.CountryCode, Valid: true},
Bet365ID: pgtype.Int4{Int32: l.Bet365ID, Valid: true},
IsActive: pgtype.Bool{Bool: l.IsActive, Valid: true},
IsFeatured: pgtype.Bool{Bool: l.IsFeatured, Valid: true},
SportID: l.SportID,
})
} }
func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error) { func (s *Store) SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error {
return s.queries.InsertLeagueSettings(ctx, domain.ConvertCreateLeagueSettings(leagueSettings))
}
func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error) {
l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{ l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{
CountryCode: pgtype.Text{ CountryCode: filter.CountryCode.ToPG(),
String: filter.CountryCode.Value, SportID: filter.SportID.ToPG(),
Valid: filter.CountryCode.Valid,
},
SportID: pgtype.Int4{
Int32: filter.SportID.Value,
Valid: filter.SportID.Valid,
},
IsActive: pgtype.Bool{
Bool: filter.IsActive.Value,
Valid: filter.IsActive.Valid,
},
IsFeatured: pgtype.Bool{
Bool: filter.IsFeatured.Value,
Valid: filter.IsFeatured.Valid,
},
Limit: pgtype.Int4{ Limit: pgtype.Int4{
Int32: int32(filter.Limit.Value), Int32: int32(filter.Limit.Value),
Valid: filter.Limit.Valid, Valid: filter.Limit.Valid,
@ -51,85 +33,38 @@ func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) (
return nil, err return nil, err
} }
leagues := make([]domain.League, len(l)) return domain.ConvertDBBaseLeagues(l), nil
for i, league := range l {
leagues[i] = domain.League{
ID: league.ID,
Name: league.Name,
CountryCode: league.CountryCode.String,
Bet365ID: league.Bet365ID.Int32,
IsActive: league.IsActive.Bool,
IsFeatured: league.IsFeatured.Bool,
SportID: league.SportID,
}
}
return leagues, nil
} }
func (s *Store) GetFeaturedLeagues(ctx context.Context) ([]domain.League, error) { func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error) {
l, err := s.queries.GetFeaturedLeagues(ctx) l, err := s.queries.GetAllLeaguesWithSettings(ctx, dbgen.GetAllLeaguesWithSettingsParams{
CompanyID: companyID,
CountryCode: filter.CountryCode.ToPG(),
SportID: filter.SportID.ToPG(),
Limit: pgtype.Int4{
Int32: int32(filter.Limit.Value),
Valid: filter.Limit.Valid,
},
Offset: pgtype.Int4{
Int32: int32(filter.Offset.Value * filter.Limit.Value),
Valid: filter.Offset.Valid,
},
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
leagues := make([]domain.League, len(l)) return domain.ConvertDBLeagueWithSettings(l), nil
for i, league := range l {
leagues[i] = domain.League{
ID: league.ID,
Name: league.Name,
CountryCode: league.CountryCode.String,
Bet365ID: league.Bet365ID.Int32,
IsActive: league.IsActive.Bool,
SportID: league.SportID,
}
}
return leagues, nil
} }
func (s *Store) CheckLeagueSupport(ctx context.Context, leagueID int64) (bool, error) { func (s *Store) CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) {
return s.queries.CheckLeagueSupport(ctx, leagueID) return s.queries.CheckLeagueSupport(ctx, dbgen.CheckLeagueSupportParams{
} LeagueID: leagueID,
CompanyID: companyID,
func (s *Store) SetLeagueActive(ctx context.Context, leagueId int64, isActive bool) error {
return s.queries.SetLeagueActive(ctx, dbgen.SetLeagueActiveParams{
ID: leagueId,
IsActive: pgtype.Bool{
Bool: isActive,
Valid: true,
},
}) })
} }
func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error { func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {
err := s.queries.UpdateLeague(ctx, dbgen.UpdateLeagueParams{ return s.queries.UpdateLeague(ctx, domain.ConvertUpdateLeague(league))
ID: league.ID,
Name: pgtype.Text{
String: league.Name.Value,
Valid: league.Name.Valid,
},
CountryCode: pgtype.Text{
String: league.CountryCode.Value,
Valid: league.CountryCode.Valid,
},
Bet365ID: pgtype.Int4{
Int32: league.Bet365ID.Value,
Valid: league.Bet365ID.Valid,
},
IsActive: pgtype.Bool{
Bool: league.IsActive.Value,
Valid: league.IsActive.Valid,
},
IsFeatured: pgtype.Bool{
Bool: league.IsFeatured.Value,
Valid: league.IsFeatured.Valid,
},
SportID: pgtype.Int4{
Int32: league.SportID.Value,
Valid: league.SportID.Valid,
},
})
return err
} }

View File

@ -3,7 +3,6 @@ package repository
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"strconv" "strconv"
@ -14,57 +13,26 @@ import (
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
func (s *Store) SaveNonLiveMarket(ctx context.Context, m domain.Market) error { func (s *Store) SaveOddMarket(ctx context.Context, m domain.CreateOddMarket) error {
if len(m.Odds) == 0 { if len(m.Odds) == 0 {
return nil return nil
} }
for _, item := range m.Odds { params, err := domain.ConvertCreateOddMarket(m)
var name string
var oddsVal float64
if m.Source == "bwin" { if err != nil {
nameValue := getMap(item["name"]) return err
name = getString(nameValue["value"])
oddsVal = getFloat(item["odds"])
} else {
name = getString(item["name"])
oddsVal = getConvertedFloat(item["odds"])
}
handicap := getString(item["handicap"])
rawOddsBytes, _ := json.Marshal(m.Odds)
params := dbgen.InsertNonLiveOddParams{
EventID: pgtype.Text{String: m.EventID, Valid: m.EventID != ""},
Fi: pgtype.Text{String: m.FI, Valid: m.FI != ""},
MarketType: m.MarketType,
MarketName: pgtype.Text{String: m.MarketName, Valid: m.MarketName != ""},
MarketCategory: pgtype.Text{String: m.MarketCategory, Valid: m.MarketCategory != ""},
MarketID: pgtype.Text{String: m.MarketID, Valid: m.MarketID != ""},
Name: pgtype.Text{String: name, Valid: name != ""},
Handicap: pgtype.Text{String: handicap, Valid: handicap != ""},
OddsValue: pgtype.Float8{Float64: oddsVal, Valid: oddsVal != 0},
Section: m.MarketCategory,
Category: pgtype.Text{Valid: false},
RawOdds: rawOddsBytes,
IsActive: pgtype.Bool{Bool: true, Valid: true},
Source: pgtype.Text{String: m.Source, Valid: true},
FetchedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
ExpiresAt: pgtype.Timestamp{Time: (time.Now()).Add(time.Hour), Valid: true},
} }
err := s.queries.InsertNonLiveOdd(ctx, params) err = s.queries.InsertOddsMarket(ctx, params)
fmt.Printf("Inserting Non Live Odd")
if err != nil { if err != nil {
_ = writeFailedMarketLog(m, err) _ = writeFailedMarketLog(m, err)
continue return err
}
} }
return nil return nil
} }
func writeFailedMarketLog(m domain.Market, err error) error { func writeFailedMarketLog(m domain.CreateOddMarket, err error) error {
logDir := "logs" logDir := "logs"
logFile := logDir + "/failed_markets.log" logFile := logDir + "/failed_markets.log"
@ -81,7 +49,7 @@ func writeFailedMarketLog(m domain.Market, err error) error {
entry := struct { entry := struct {
Time string `json:"time"` Time string `json:"time"`
Error string `json:"error"` Error string `json:"error"`
Record domain.Market `json:"record"` Record domain.CreateOddMarket `json:"record"`
}{ }{
Time: time.Now().Format(time.RFC3339), Time: time.Now().Format(time.RFC3339),
Error: err.Error(), Error: err.Error(),
@ -93,196 +61,130 @@ func writeFailedMarketLog(m domain.Market, err error) error {
return writeErr return writeErr
} }
func (s *Store) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { func (s *Store) GetAllOdds(ctx context.Context, filter domain.OddMarketFilter) ([]domain.OddMarket, error) {
odds, err := s.queries.GetPrematchOdds(ctx) rows, err := s.queries.GetAllOdds(ctx, dbgen.GetAllOddsParams{
Offset: filter.Offset.ToPG(),
Limit: filter.Limit.ToPG(),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
domainOdds := make([]domain.Odd, len(odds)) domainOdds, err := domain.ConvertDBOddMarkets(rows)
for i, odd := range odds {
domainOdds[i] = domain.Odd{ if err != nil {
EventID: odd.EventID.String, return nil, err
Fi: odd.Fi.String,
MarketType: odd.MarketType,
MarketName: odd.MarketName.String,
MarketCategory: odd.MarketCategory.String,
MarketID: odd.MarketID.String,
Name: odd.Name.String,
Handicap: odd.Handicap.String,
OddsValue: odd.OddsValue.Float64,
Section: odd.Section,
Category: odd.Category.String,
RawOdds: func() []json.RawMessage {
var rawOdds []json.RawMessage
if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil {
rawOdds = nil
}
return rawOdds
}(),
FetchedAt: odd.FetchedAt.Time,
ExpiresAt: odd.ExpiresAt.Time,
Source: odd.Source.String,
IsActive: odd.IsActive.Bool,
}
} }
return domainOdds, nil return domainOdds, nil
} }
func (s *Store) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) { func (s *Store) GetAllOddsWithSettings(ctx context.Context, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) {
rows, err := s.queries.GetALLPrematchOdds(ctx) odds, err := s.queries.GetAllOddsWithSettings(ctx, dbgen.GetAllOddsWithSettingsParams{
CompanyID: companyID,
Offset: filter.Offset.ToPG(),
Limit: filter.Limit.ToPG(),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
domainOdds := make([]domain.Odd, len(rows)) domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds)
for i, row := range rows {
domainOdds[i] = domain.Odd{ if err != nil {
// ID: int64(row.ID), return nil, err
EventID: row.EventID.String,
Fi: row.Fi.String,
MarketType: row.MarketType,
MarketName: row.MarketName.String,
MarketCategory: row.MarketCategory.String,
MarketID: row.MarketID.String,
Name: row.Name.String,
Handicap: row.Handicap.String,
OddsValue: row.OddsValue.Float64,
Section: row.Section,
Category: row.Category.String,
RawOdds: func() []json.RawMessage {
var rawOdds []json.RawMessage
if err := json.Unmarshal(row.RawOdds, &rawOdds); err != nil {
rawOdds = nil
}
return rawOdds
}(),
FetchedAt: row.FetchedAt.Time,
ExpiresAt: row.ExpiresAt.Time,
Source: row.Source.String,
IsActive: row.IsActive.Bool,
}
} }
return domainOdds, nil return domainOdds, nil
} }
func (s *Store) GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.RawOddsByMarketID, error) { func (s *Store) GetOddsByMarketID(ctx context.Context, marketID string, eventID string) (domain.OddMarket, error) {
params := dbgen.GetOddsByMarketIDParams{
MarketID: pgtype.Text{String: marketID, Valid: true},
Fi: pgtype.Text{String: upcomingID, Valid: true},
}
odds, err := s.queries.GetOddsByMarketID(ctx, params) odds, err := s.queries.GetOddsByMarketID(ctx, dbgen.GetOddsByMarketIDParams{
MarketID: marketID,
EventID: eventID,
})
if err != nil { if err != nil {
return domain.RawOddsByMarketID{}, err return domain.OddMarket{}, err
} }
var rawOdds []json.RawMessage convertedOdd, err := domain.ConvertDBOddMarket(odds)
if err := json.Unmarshal(odds.RawOdds, &rawOdds); err != nil {
return domain.RawOddsByMarketID{}, err
}
return domain.RawOddsByMarketID{ if err != nil {
ID: int64(odds.ID), return domain.OddMarket{}, err
MarketName: odds.MarketName.String,
Handicap: odds.Handicap.String,
RawOdds: func() []json.RawMessage {
converted := make([]json.RawMessage, len(rawOdds))
for i, r := range rawOdds {
converted[i] = json.RawMessage(r)
} }
return converted return convertedOdd, nil
}(),
FetchedAt: odds.FetchedAt.Time,
ExpiresAt: odds.ExpiresAt.Time,
}, nil
} }
func (s *Store) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit domain.ValidInt32, offset domain.ValidInt32) ([]domain.Odd, error) { func (s *Store) GetOddsWithSettingsByMarketID(ctx context.Context, marketID string, eventID string, companyID int64) (domain.OddMarketWithSettings, error) {
odds, err := s.queries.GetPaginatedPrematchOddsByUpcomingID(ctx, dbgen.GetPaginatedPrematchOddsByUpcomingIDParams{
ID: upcomingID, odds, err := s.queries.GetOddsWithSettingsByMarketID(ctx, dbgen.GetOddsWithSettingsByMarketIDParams{
Limit: limit.ToPG(), MarketID: marketID,
Offset: offset.ToPG(), EventID: eventID,
CompanyID: companyID,
})
if err != nil {
return domain.OddMarketWithSettings{}, err
}
convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds)
if err != nil {
return domain.OddMarketWithSettings{}, err
}
return convertedOdd, nil
}
func (s *Store) GetOddsByEventID(ctx context.Context, upcomingID string, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) {
odds, err := s.queries.GetOddsByEventID(ctx, dbgen.GetOddsByEventIDParams{
EventID: upcomingID,
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{},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Map the results to domain.Odd // Map the results to domain.Odd
domainOdds := make([]domain.Odd, len(odds)) domainOdds, err := domain.ConvertDBOddMarkets(odds)
for i, odd := range odds { if err != nil {
var rawOdds []json.RawMessage return nil, err
if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil {
rawOdds = nil
}
domainOdds[i] = domain.Odd{
EventID: odd.EventID.String,
Fi: odd.Fi.String,
MarketType: odd.MarketType,
MarketName: odd.MarketName.String,
MarketCategory: odd.MarketCategory.String,
MarketID: odd.MarketID.String,
Name: odd.Name.String,
Handicap: odd.Handicap.String,
OddsValue: odd.OddsValue.Float64,
Section: odd.Section,
Category: odd.Category.String,
RawOdds: rawOdds,
FetchedAt: odd.FetchedAt.Time,
ExpiresAt: odd.ExpiresAt.Time,
Source: odd.Source.String,
IsActive: odd.IsActive.Bool,
}
} }
return domainOdds, nil return domainOdds, nil
} }
func (s *Store) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.Odd, error) { func (s *Store) GetOddsWithSettingsByEventID(ctx context.Context, upcomingID string, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) {
odds, err := s.queries.GetPrematchOddsByUpcomingID(ctx, upcomingID) odds, err := s.queries.GetOddsWithSettingsByEventID(ctx, dbgen.GetOddsWithSettingsByEventIDParams{
EventID: upcomingID,
CompanyID: companyID,
Offset: filter.Offset.ToPG(),
Limit: filter.Limit.ToPG(),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Map the results to domain.Odd // Map the results to domain.Odd
domainOdds := make([]domain.Odd, len(odds)) domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds)
for i, odd := range odds { if err != nil {
var rawOdds []json.RawMessage return nil, err
if err := json.Unmarshal(odd.RawOdds, &rawOdds); err != nil {
rawOdds = nil
}
domainOdds[i] = domain.Odd{
EventID: odd.EventID.String,
Fi: odd.Fi.String,
MarketType: odd.MarketType,
MarketName: odd.MarketName.String,
MarketCategory: odd.MarketCategory.String,
MarketID: odd.MarketID.String,
Name: odd.Name.String,
Handicap: odd.Handicap.String,
OddsValue: odd.OddsValue.Float64,
Section: odd.Section,
Category: odd.Category.String,
RawOdds: rawOdds,
FetchedAt: odd.FetchedAt.Time,
ExpiresAt: odd.ExpiresAt.Time,
Source: odd.Source.String,
IsActive: odd.IsActive.Bool,
}
} }
return domainOdds, nil return domainOdds, nil
} }
func (s *Store) DeleteOddsForEvent(ctx context.Context, eventID string) error { func (s *Store) DeleteOddsForEvent(ctx context.Context, eventID string) error {
return s.queries.DeleteOddsForEvent(ctx, pgtype.Text{ return s.queries.DeleteOddsForEvent(ctx, eventID)
String: eventID,
Valid: true,
})
} }
func getString(v interface{}) string { func getString(v interface{}) string {

View File

@ -46,6 +46,8 @@ func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int6
Time: time.Now(), Time: time.Now(),
Valid: true, Valid: true,
}, },
CompanyID: user.CompanyID.ToPG(),
Suspended: user.Suspended,
}) })
if err != nil { if err != nil {
return domain.User{}, err return domain.User{}, err
@ -57,6 +59,15 @@ func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int6
Email: userRes.Email.String, Email: userRes.Email.String,
PhoneNumber: userRes.PhoneNumber.String, PhoneNumber: userRes.PhoneNumber.String,
Role: domain.Role(userRes.Role), Role: domain.Role(userRes.Role),
CompanyID: domain.ValidInt64{
Value: userRes.CompanyID.Int64,
Valid: userRes.CompanyID.Valid,
},
EmailVerified: userRes.EmailVerified,
PhoneVerified: userRes.PhoneVerified,
CreatedAt: userRes.CreatedAt.Time,
UpdatedAt: userRes.UpdatedAt.Time,
Suspended: userRes.Suspended,
}, nil }, nil
} }
func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) { func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) {
@ -247,18 +258,14 @@ func (s *Store) GetCashiersByBranch(ctx context.Context, branchID int64) ([]doma
func (s *Store) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) { func (s *Store) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) {
query := dbgen.SearchUserByNameOrPhoneParams{ query := dbgen.SearchUserByNameOrPhoneParams{
Column1: pgtype.Text{ CompanyID: companyID.ToPG(),
Column2: pgtype.Text{
String: searchString, String: searchString,
Valid: true, Valid: true,
}, },
CompanyID: pgtype.Int8{
Int64: companyID.Value,
Valid: companyID.Valid,
},
} }
if role != nil { if role != nil {
query.Role = pgtype.Text{ query.Role = pgtype.Text{
String: string(*role), String: string(*role),
Valid: true, Valid: true,
@ -340,7 +347,7 @@ func (s *Store) DeleteUser(ctx context.Context, id int64) error {
} }
return nil return nil
} }
func (s *Store) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) { func (s *Store) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error) {
row, err := s.queries.CheckPhoneEmailExist(ctx, dbgen.CheckPhoneEmailExistParams{ row, err := s.queries.CheckPhoneEmailExist(ctx, dbgen.CheckPhoneEmailExistParams{
PhoneNumber: pgtype.Text{ PhoneNumber: pgtype.Text{
@ -352,6 +359,7 @@ func (s *Store) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string
Valid: email != "", Valid: email != "",
}, },
CompanyID: companyID.ToPG(),
}) })
if err != nil { if err != nil {
@ -360,10 +368,13 @@ func (s *Store) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string
return row.EmailExists, row.PhoneExists, nil return row.EmailExists, row.PhoneExists, nil
} }
func (s *Store) GetUserByEmail(ctx context.Context, email string) (domain.User, error) { func (s *Store) GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error) {
user, err := s.queries.GetUserByEmail(ctx, pgtype.Text{ user, err := s.queries.GetUserByEmail(ctx, dbgen.GetUserByEmailParams{
Email: pgtype.Text{
String: email, String: email,
Valid: true, Valid: true,
},
CompanyID: companyID.ToPG(),
}) })
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
@ -386,10 +397,13 @@ func (s *Store) GetUserByEmail(ctx context.Context, email string) (domain.User,
SuspendedAt: user.SuspendedAt.Time, SuspendedAt: user.SuspendedAt.Time,
}, nil }, nil
} }
func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error) { func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error) {
user, err := s.queries.GetUserByPhone(ctx, pgtype.Text{ user, err := s.queries.GetUserByPhone(ctx, dbgen.GetUserByPhoneParams{
PhoneNumber: pgtype.Text{
String: phoneNum, String: phoneNum,
Valid: true, Valid: true,
},
CompanyID: companyID.ToPG(),
}) })
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {

View File

@ -26,8 +26,8 @@ type LoginSuccess struct {
CompanyID domain.ValidInt64 CompanyID domain.ValidInt64
} }
func (s *Service) Login(ctx context.Context, email, phone string, password string) (LoginSuccess, error) { func (s *Service) Login(ctx context.Context, email, phone string, password string, companyID domain.ValidInt64) (LoginSuccess, error) {
user, err := s.userStore.GetUserByEmailPhone(ctx, email, phone) user, err := s.userStore.GetUserByEmailPhone(ctx, email, phone, companyID)
if err != nil { if err != nil {
return LoginSuccess{}, err return LoginSuccess{}, err
} }

View File

@ -7,7 +7,7 @@ import (
) )
type UserStore interface { type UserStore interface {
GetUserByEmailPhone(ctx context.Context, email, phone string) (domain.User, error) GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error)
} }
type TokenStore interface { type TokenStore interface {
CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error

View File

@ -133,7 +133,7 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
return domain.CreateBetOutcome{}, ErrEventHasNotEnded return domain.CreateBetOutcome{}, ErrEventHasNotEnded
} }
odds, err := s.prematchSvc.GetRawOddsByMarketID(ctx, marketIDStr, eventIDStr) odds, err := s.prematchSvc.GetOddsByMarketID(ctx, marketIDStr, eventIDStr)
if err != nil { if err != nil {
s.mongoLogger.Error("failed to get raw odds by market ID", s.mongoLogger.Error("failed to get raw odds by market ID",
zap.Int64("event_id", eventID), zap.Int64("event_id", eventID),
@ -581,7 +581,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
var newOdds []domain.CreateBetOutcome var newOdds []domain.CreateBetOutcome
var totalOdds float32 = 1 var totalOdds float32 = 1
markets, err := s.prematchSvc.GetPrematchOddsByUpcomingID(ctx, eventID) markets, err := s.prematchSvc.GetOddsByEventID(ctx, eventID, domain.OddMarketWithEventFilter{})
if err != nil { if err != nil {
s.logger.Error("failed to get odds for event", "event id", eventID, "error", err) s.logger.Error("failed to get odds for event", "event id", eventID, "error", err)
s.mongoLogger.Error("failed to get odds for event", s.mongoLogger.Error("failed to get odds for event",
@ -603,7 +603,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
return nil, 0, fmt.Errorf("empty odds or event %v", eventID) return nil, 0, fmt.Errorf("empty odds or event %v", eventID)
} }
var selectedMarkets []domain.Odd var selectedMarkets []domain.OddMarket
numMarkets = min(numMarkets, len(markets)) numMarkets = min(numMarkets, len(markets))
for i := 0; i < numMarkets; i++ { for i := 0; i < numMarkets; i++ {
randomIndex := random.Intn(len(markets)) randomIndex := random.Intn(len(markets))
@ -714,7 +714,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
return newOdds, totalOdds, nil return newOdds, totalOdds, nil
} }
func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, leagueID, sportID domain.ValidInt32, firstStartTime, lastStartTime domain.ValidTime) (domain.CreateBetRes, error) { func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, leagueID domain.ValidInt64, sportID domain.ValidInt32, firstStartTime, lastStartTime domain.ValidTime) (domain.CreateBetRes, error) {
// Get a unexpired event id // Get a unexpired event id
@ -742,7 +742,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le
} }
// TODO: Add the option of passing number of created events // TODO: Add the option of passing number of created events
var selectedUpcomingEvents []domain.UpcomingEvent var selectedUpcomingEvents []domain.BaseEvent
numEventsPerBet := min(random.Intn(4)+1, len(events)) //Eliminate the option of 0 numEventsPerBet := min(random.Intn(4)+1, len(events)) //Eliminate the option of 0
for i := 0; i < int(numEventsPerBet); i++ { for i := 0; i < int(numEventsPerBet); i++ {

View File

@ -11,6 +11,7 @@ type CompanyStore interface {
GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error) GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error)
SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error)
GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error)
GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error)
UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error)
DeleteCompany(ctx context.Context, id int64) error DeleteCompany(ctx context.Context, id int64) error

View File

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

View File

@ -7,16 +7,18 @@ import (
) )
type Service interface { type Service interface {
FetchLiveEvents(ctx context.Context) error // FetchLiveEvents(ctx context.Context) error
FetchUpcomingEvents(ctx context.Context) error FetchUpcomingEvents(ctx context.Context) error
GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) GetAllUpcomingEvents(ctx context.Context) ([]domain.BaseEvent, error)
GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, error) GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, error)
GetPaginatedUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, int64, error) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error)
GetUpcomingEventByID(ctx context.Context, ID string) (domain.UpcomingEvent, error) GetUpcomingEventByID(ctx context.Context, ID string) (domain.BaseEvent, error)
// GetAndStoreMatchResult(ctx context.Context, eventID string) error // GetAndStoreMatchResult(ctx context.Context, eventID string) error
UpdateFinalScore(ctx context.Context, eventID, fullScore string, status domain.EventStatus) error UpdateFinalScore(ctx context.Context, eventID, fullScore string, status domain.EventStatus) error
UpdateEventStatus(ctx context.Context, eventID string, status domain.EventStatus) error UpdateEventStatus(ctx context.Context, eventID string, status domain.EventStatus) error
UpdateEventFeatured(ctx context.Context, eventID string, flagged bool) error
IsEventMonitored(ctx context.Context, eventID string) (bool, error) IsEventMonitored(ctx context.Context, eventID string) (bool, error)
UpdateEventMonitored(ctx context.Context, eventID string, IsMonitored bool) error UpdateEventMonitored(ctx context.Context, eventID string, IsMonitored bool) error
GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error)
GetEventWithSettingByID(ctx context.Context, ID string, companyID int64) (domain.EventWithSettings, error)
UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error
} }

View File

@ -32,162 +32,162 @@ func New(token string, store *repository.Store, mongoLogger *zap.Logger) Service
} }
} }
func (s *service) FetchLiveEvents(ctx context.Context) error { // func (s *service) FetchLiveEvents(ctx context.Context) error {
var wg sync.WaitGroup // var wg sync.WaitGroup
urls := []struct { // urls := []struct {
name string // name string
source string // source string
}{ // }{
{"https://api.b365api.com/v1/bet365/inplay?sport_id=%d&token=%s", "bet365"}, // {"https://api.b365api.com/v1/bet365/inplay?sport_id=%d&token=%s", "bet365"},
{"https://api.b365api.com/v1/betfair/sb/inplay?sport_id=%d&token=%s", "betfair"}, // {"https://api.b365api.com/v1/betfair/sb/inplay?sport_id=%d&token=%s", "betfair"},
{"https://api.b365api.com/v1/1xbet/inplay?sport_id=%d&token=%s", "1xbet"}, // {"https://api.b365api.com/v1/1xbet/inplay?sport_id=%d&token=%s", "1xbet"},
} // }
for _, url := range urls { // for _, url := range urls {
wg.Add(1) // wg.Add(1)
go func() { // go func() {
defer wg.Done() // defer wg.Done()
s.fetchLiveEvents(ctx, url.name, url.source) // s.fetchLiveEvents(ctx, url.name, url.source)
}() // }()
} // }
wg.Wait() // wg.Wait()
return nil // return nil
} // }
func (s *service) fetchLiveEvents(ctx context.Context, url, source string) error { // func (s *service) fetchLiveEvents(ctx context.Context, url, source string) error {
sportIDs := []int{1, 13, 78, 18, 91, 16, 17, 14, 12, 3, 2, 4, 83, 15, 92, 94, 8, 19, 36, 66, 9, 75, 90, 95, 110, 107, 151, 162, 148} // sportIDs := []int{1, 13, 78, 18, 91, 16, 17, 14, 12, 3, 2, 4, 83, 15, 92, 94, 8, 19, 36, 66, 9, 75, 90, 95, 110, 107, 151, 162, 148}
var wg sync.WaitGroup // var wg sync.WaitGroup
for _, sportID := range sportIDs { // for _, sportID := range sportIDs {
wg.Add(1) // wg.Add(1)
go func(sportID int) { // go func(sportID int) {
defer wg.Done() // defer wg.Done()
url := fmt.Sprintf(url, sportID, s.token) // url := fmt.Sprintf(url, sportID, s.token)
resp, err := http.Get(url) // resp, err := http.Get(url)
if err != nil { // if err != nil {
fmt.Printf(" Failed request for sport_id=%d: %v\n", sportID, err) // fmt.Printf(" Failed request for sport_id=%d: %v\n", sportID, err)
return // return
} // }
defer resp.Body.Close() // defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body) // body, _ := io.ReadAll(resp.Body)
events := []domain.Event{} // events := []domain.Event{}
switch source { // switch source {
case "bet365": // case "bet365":
events = handleBet365prematch(body, sportID, source) // events = handleBet365prematch(body, sportID, source)
case "betfair": // case "betfair":
events = handleBetfairprematch(body, sportID, source) // events = handleBetfairprematch(body, sportID, source)
case "1xbet": // case "1xbet":
// betfair and 1xbet have the same result structure // // betfair and 1xbet have the same result structure
events = handleBetfairprematch(body, sportID, source) // events = handleBetfairprematch(body, sportID, source)
} // }
for _, event := range events { // for _, event := range events {
if err := s.store.SaveEvent(ctx, event); err != nil { // if err := s.store.SaveEvent(ctx, event); err != nil {
fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) // fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err)
} // }
} // }
}(sportID) // }(sportID)
} // }
wg.Wait() // wg.Wait()
fmt.Println("All live events fetched and stored.") // fmt.Println("All live events fetched and stored.")
return nil // return nil
} // }
func handleBet365prematch(body []byte, sportID int, source string) []domain.Event { // func handleBet365prematch(body []byte, sportID int, source string) []domain.Event {
var data struct { // var data struct {
Success int `json:"success"` // Success int `json:"success"`
Results [][]map[string]interface{} `json:"results"` // Results [][]map[string]interface{} `json:"results"`
} // }
events := []domain.Event{} // events := []domain.Event{}
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { // if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
fmt.Printf("%s: Decode failed for sport_id=%d\nRaw: %s\n", source, sportID, string(body)) // fmt.Printf("%s: Decode failed for sport_id=%d\nRaw: %s\n", source, sportID, string(body))
return events // return events
} // }
for _, group := range data.Results { // for _, group := range data.Results {
for _, ev := range group { // for _, ev := range group {
if getString(ev["type"]) != "EV" { // if getString(ev["type"]) != "EV" {
continue // continue
} // }
event := domain.Event{ // event := domain.Event{
ID: getString(ev["ID"]), // ID: getString(ev["ID"]),
SportID: int32(sportID), // SportID: int32(sportID),
MatchName: getString(ev["NA"]), // MatchName: getString(ev["NA"]),
Score: getString(ev["SS"]), // Score: getString(ev["SS"]),
MatchMinute: getInt(ev["TM"]), // MatchMinute: getInt(ev["TM"]),
TimerStatus: getString(ev["TT"]), // TimerStatus: getString(ev["TT"]),
HomeTeamID: getInt32(ev["HT"]), // HomeTeamID: getInt32(ev["HT"]),
AwayTeamID: getInt32(ev["AT"]), // AwayTeamID: getInt32(ev["AT"]),
HomeKitImage: getString(ev["K1"]), // HomeKitImage: getString(ev["K1"]),
AwayKitImage: getString(ev["K2"]), // AwayKitImage: getString(ev["K2"]),
LeagueName: getString(ev["CT"]), // LeagueName: getString(ev["CT"]),
LeagueID: getInt32(ev["C2"]), // LeagueID: getInt32(ev["C2"]),
LeagueCC: getString(ev["CB"]), // LeagueCC: getString(ev["CB"]),
StartTime: time.Now().UTC().Format(time.RFC3339), // StartTime: time.Now().UTC().Format(time.RFC3339),
IsLive: true, // IsLive: true,
Status: "live", // Status: "live",
MatchPeriod: getInt(ev["MD"]), // MatchPeriod: getInt(ev["MD"]),
AddedTime: getInt(ev["TA"]), // AddedTime: getInt(ev["TA"]),
Source: source, // Source: source,
} // }
events = append(events, event) // events = append(events, event)
} // }
} // }
return events // return events
} // }
func handleBetfairprematch(body []byte, sportID int, source string) []domain.Event { // func handleBetfairprematch(body []byte, sportID int, source string) []domain.Event {
var data struct { // var data struct {
Success int `json:"success"` // Success int `json:"success"`
Results []map[string]interface{} `json:"results"` // Results []map[string]interface{} `json:"results"`
} // }
events := []domain.Event{} // events := []domain.Event{}
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { // if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
fmt.Printf("%s: Decode failed for sport_id=%d\nRaw: %s\n", source, sportID, string(body)) // fmt.Printf("%s: Decode failed for sport_id=%d\nRaw: %s\n", source, sportID, string(body))
return events // return events
} // }
for _, ev := range data.Results { // for _, ev := range data.Results {
homeRaw, _ := ev["home"].(map[string]interface{}) // homeRaw, _ := ev["home"].(map[string]interface{})
awayRaw, _ := ev["home"].(map[string]interface{}) // awayRaw, _ := ev["home"].(map[string]interface{})
event := domain.Event{ // event := domain.Event{
ID: getString(ev["id"]), // ID: getString(ev["id"]),
SportID: int32(sportID), // SportID: int32(sportID),
TimerStatus: getString(ev["time_status"]), // TimerStatus: getString(ev["time_status"]),
HomeTeamID: getInt32(homeRaw["id"]), // HomeTeamID: getInt32(homeRaw["id"]),
AwayTeamID: getInt32(awayRaw["id"]), // AwayTeamID: getInt32(awayRaw["id"]),
StartTime: time.Now().UTC().Format(time.RFC3339), // StartTime: time.Now().UTC().Format(time.RFC3339),
IsLive: true, // IsLive: true,
Status: "live", // Status: "live",
Source: source, // Source: source,
} // }
events = append(events, event) // events = append(events, event)
} // }
return events // return events
} // }
func (s *service) FetchUpcomingEvents(ctx context.Context) error { func (s *service) FetchUpcomingEvents(ctx context.Context) error {
var wg sync.WaitGroup var wg sync.WaitGroup
urls := []struct { urls := []struct {
name string name string
source string source domain.EventSource
}{ }{
{"https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s&page=%d", "bet365"}, {"https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s&page=%d", domain.EVENT_SOURCE_BET365},
// {"https://api.b365api.com/v1/betfair/sb/upcoming?sport_id=%d&token=%s&page=%d", "betfair"}, // {"https://api.b365api.com/v1/betfair/sb/upcoming?sport_id=%d&token=%s&page=%d", "betfair"},
// {"https://api.b365api.com/v1/1xbet/upcoming?sport_id=%d&token=%s&page=%d", "1xbet"}, // {"https://api.b365api.com/v1/1xbet/upcoming?sport_id=%d&token=%s&page=%d", "1xbet"},
} }
@ -205,38 +205,38 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error {
return nil return nil
} }
func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_url, source string) { func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_url string, source domain.EventSource) {
const pageLimit int = 200
sportIDs := []int{1, 18, 17, 3, 83, 15, 12, 19, 8, 16, 91} sportIDs := []int{1, 18, 17, 3, 83, 15, 12, 19, 8, 16, 91}
// sportIDs := []int{1} // sportIDs := []int{1}
// TODO: Add the league skipping again when we have dynamic leagues
// b, err := os.OpenFile("logs/skipped_leagues.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
// if err != nil {
// log.Printf("❌ Failed to open leagues file %v", err)
// return
// }
for sportIndex, sportID := range sportIDs { for sportIndex, sportID := range sportIDs {
var totalPages int = 1 var totalPages int = 1
var page int = 0 var page int = 0
var limit int = 200
var count int = 0 var count int = 0
var skippedLeague []string var skippedLeague []string
var totalEvents = 0 var totalEvents = 0
logger := s.mongoLogger.With(
zap.String("source", string(source)),
zap.Int("sport_id", sportID),
zap.String("sport_name", domain.Sport(sportID).String()),
zap.Int("count", count),
zap.Int("totalEvents", totalEvents),
zap.Int("Skipped leagues", len(skippedLeague)),
)
for page <= totalPages { for page <= totalPages {
page = page + 1 page = page + 1
url := fmt.Sprintf(source_url, sportID, s.token, page) url := fmt.Sprintf(source_url, sportID, s.token, page)
log.Printf("📡 Fetching data from %s - sport %d (%d/%d), for event data page (%d/%d)", log.Printf("📡 Fetching data from %s - sport %d (%d/%d), for event data page (%d/%d)",
source, sportID, sportIndex+1, len(sportIDs), page, totalPages) source, sportID, sportIndex+1, len(sportIDs), page, totalPages)
resp, err := http.Get(url) eventLogger := logger.With(
if err != nil {
s.mongoLogger.Error(
"Failed to fetch event data for page",
zap.String("source", source),
zap.Int("sport_id", sportID),
zap.Int("page", page), zap.Int("page", page),
zap.Int("total_pages", totalPages), zap.Int("total_pages", totalPages),
zap.Error(err),
) )
resp, err := http.Get(url)
if err != nil {
eventLogger.Error("Failed to fetch event data for page", zap.Error(err))
continue continue
} }
defer resp.Body.Close() defer resp.Body.Close()
@ -244,56 +244,31 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
s.mongoLogger.Error( eventLogger.Error("Failed to read event response body", zap.Error(err))
"Failed to read event response body",
zap.String("source", source),
zap.Int("sport_id", sportID),
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Error(err),
)
continue continue
} }
var data domain.BetResult var data domain.B365UpcomingRes
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
s.mongoLogger.Error( eventLogger.Error("Failed to parse event json data", zap.Error(err))
"Failed to parse event json data",
zap.String("source", source),
zap.Int("sport_id", sportID),
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Error(err),
)
continue continue
} }
for _, ev := range data.Results { for _, ev := range data.Results {
startUnix, err := strconv.ParseInt(ev.Time, 10, 64) startUnix, err := strconv.ParseInt(ev.Time, 10, 64)
if err != nil { dataLogger := eventLogger.With(
s.mongoLogger.Error(
"Invalid time",
zap.String("time", ev.Time), zap.String("time", ev.Time),
zap.String("source", source), zap.String("leagueID", ev.League.ID),
zap.Int("sport_id", sportID), zap.String("leagueName", ev.League.Name),
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Error(err),
) )
if err != nil {
dataLogger.Error("Invalid time", zap.Error(err))
continue continue
} }
leagueID, err := strconv.ParseInt(ev.League.ID, 10, 64) leagueID, err := strconv.ParseInt(ev.League.ID, 10, 64)
if err != nil { if err != nil {
s.mongoLogger.Error( dataLogger.Error("Invalid league id", zap.Error(err))
"Invalid league id",
zap.String("leagueID", ev.League.ID),
zap.String("source", source),
zap.Int("sport_id", sportID),
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Error(err),
)
continue continue
} }
@ -301,87 +276,67 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur
// no this its fine to keep it here // no this its fine to keep it here
// but change the league id to bet365 id later // but change the league id to bet365 id later
//Automatically feature the league if its in the list //Automatically feature the league if its in the list
err = s.store.SaveLeague(ctx, domain.League{ err = s.store.SaveLeague(ctx, domain.CreateLeague{
ID: leagueID, ID: leagueID,
Name: ev.League.Name, Name: ev.League.Name,
IsActive: true, DefaultIsActive: true,
IsFeatured: slices.Contains(domain.FeaturedLeagues, leagueID), DefaultIsFeatured: slices.Contains(domain.FeaturedLeagues, leagueID),
SportID: convertInt32(ev.SportID), SportID: convertInt32(ev.SportID),
}) })
if err != nil { if err != nil {
s.mongoLogger.Error( dataLogger.Error("error while saving league", zap.Error(err))
"error while saving league",
zap.String("leagueID", ev.League.ID),
zap.String("leagueName", ev.League.Name),
zap.String("source", source),
zap.Int("sport_id", sportID),
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Error(err),
)
continue continue
} }
if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil { // Since the system is multi-vendor now, no events are going to be skipped
s.mongoLogger.Warn( // if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil {
"Skipping league", // dataLogger.Warn(
zap.String("league", ev.League.Name), // "Skipping league",
zap.Bool("is_supported", supported), // zap.Bool("is_supported", supported),
zap.Error(err), // zap.Error(err),
) // )
skippedLeague = append(skippedLeague, ev.League.Name) // skippedLeague = append(skippedLeague, ev.League.Name)
continue // continue
} // }
event := domain.UpcomingEvent{ event := domain.CreateEvent{
ID: ev.ID, ID: ev.ID,
SportID: convertInt32(ev.SportID), SportID: convertInt32(ev.SportID),
MatchName: "", MatchName: "",
HomeTeam: ev.Home.Name, HomeTeam: ev.Home.Name,
AwayTeam: "", // handle nil safely AwayTeam: "", // handle nil safely
HomeTeamID: convertInt32(ev.Home.ID), HomeTeamID: convertInt64(ev.Home.ID),
AwayTeamID: 0, AwayTeamID: 0,
HomeKitImage: "", HomeTeamImage: "",
AwayKitImage: "", AwayTeamImage: "",
LeagueID: convertInt32(ev.League.ID), LeagueID: convertInt64(ev.League.ID),
LeagueName: ev.League.Name, LeagueName: ev.League.Name,
LeagueCC: "",
StartTime: time.Unix(startUnix, 0).UTC(), StartTime: time.Unix(startUnix, 0).UTC(),
Source: source, Source: source,
IsLive: false,
Status: domain.STATUS_PENDING,
} }
if ev.Away != nil { if ev.Away != nil {
dataLogger.Info("event away is empty")
event.AwayTeam = ev.Away.Name event.AwayTeam = ev.Away.Name
event.AwayTeamID = convertInt32(ev.Away.ID) event.AwayTeamID = convertInt64(ev.Away.ID)
event.MatchName = ev.Home.Name + " vs " + ev.Away.Name event.MatchName = ev.Home.Name + " vs " + ev.Away.Name
} }
ok, err := s.CheckAndInsertEventHistory(ctx, event)
if err := s.CheckAndInsertEventHistory(ctx, event); err != nil { if err != nil {
s.mongoLogger.Error( dataLogger.Error("failed to check and insert event history", zap.Error(err))
"failed to check and insert event history",
zap.String("leagueID", ev.League.ID),
zap.String("leagueName", ev.League.Name),
zap.String("source", source),
zap.Int("sport_id", sportID),
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Error(err),
)
} }
err = s.store.SaveUpcomingEvent(ctx, event) if ok {
dataLogger.Info("event history has been recorded")
}
err = s.store.SaveEvent(ctx, event)
if err != nil { if err != nil {
s.mongoLogger.Error( dataLogger.Error("failed to save upcoming event", zap.Error(err))
"failed to save upcoming event",
zap.String("leagueID", ev.League.ID),
zap.String("leagueName", ev.League.Name),
zap.String("source", source),
zap.Int("sport_id", sportID),
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Error(err),
)
} }
totalEvents += 1 totalEvents += 1
} }
@ -391,7 +346,7 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur
totalPages = data.Pager.Total / data.Pager.PerPage totalPages = data.Pager.Total / data.Pager.PerPage
if count >= limit { if count >= pageLimit {
break break
} }
if page > totalPages { if page > totalPages {
@ -401,7 +356,7 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur
} }
s.mongoLogger.Info( s.mongoLogger.Info(
"Successfully fetched upcoming events", "Successfully fetched upcoming events",
zap.String("source", source), zap.String("source", string(source)),
zap.Int("totalEvents", totalEvents), zap.Int("totalEvents", totalEvents),
zap.Int("sport_id", sportID), zap.Int("sport_id", sportID),
zap.String("sport_name", domain.Sport(sportID).String()), zap.String("sport_name", domain.Sport(sportID).String()),
@ -413,35 +368,30 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur
} }
} }
func (s *service) CheckAndInsertEventHistory(ctx context.Context, event domain.UpcomingEvent) error { func (s *service) CheckAndInsertEventHistory(ctx context.Context, event domain.CreateEvent) (bool, error) {
isEventMonitored, err := s.store.IsEventMonitored(ctx, event.ID) isEventMonitored, err := s.store.IsEventMonitored(ctx, event.ID)
if err != nil { eventLogger := s.mongoLogger.With(
s.mongoLogger.Error(
"failed to get event is_monitored",
zap.String("eventID", event.ID), zap.String("eventID", event.ID),
zap.Int32("leagueID", event.LeagueID), zap.Int64("leagueID", event.LeagueID),
zap.String("leagueName", event.LeagueName), zap.String("leagueName", event.LeagueName),
zap.Int32("sport_id", event.SportID), zap.Int32("sport_id", event.SportID),
zap.Error(err),
) )
if err != nil {
eventLogger.Error("failed to get event is_monitored", zap.Error(err))
return false, err
} }
if !isEventMonitored { if !isEventMonitored {
return nil return false, nil
} }
oldEvent, err := s.GetUpcomingEventByID(ctx, event.ID) oldEvent, err := s.GetUpcomingEventByID(ctx, event.ID)
if err != nil { if err != nil {
s.mongoLogger.Error( eventLogger.Error("failed to get event by id", zap.Error(err))
"failed to get event by id", return false, err
zap.String("eventID", event.ID),
zap.Int32("leagueID", event.LeagueID),
zap.String("leagueName", event.LeagueName),
zap.Int32("sport_id", event.SportID),
zap.Error(err),
)
} }
if oldEvent.Status != event.Status { if oldEvent.Status != event.Status {
@ -451,20 +401,14 @@ func (s *service) CheckAndInsertEventHistory(ctx context.Context, event domain.U
}) })
if err != nil { if err != nil {
s.mongoLogger.Error( eventLogger.Error("failed to get event by id", zap.Error(err))
"failed to get event by id", return false, err
zap.String("eventID", event.ID),
zap.Int32("leagueID", event.LeagueID),
zap.String("leagueName", event.LeagueName),
zap.Int32("sport_id", event.SportID),
zap.Error(err),
)
return err
}
} }
return nil return true, nil
}
return false, nil
} }
func getString(v interface{}) string { func getString(v interface{}) string {
@ -494,19 +438,25 @@ func convertInt32(num string) int32 {
} }
return 0 return 0
} }
func (s *service) GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) { func convertInt64(num string) int64 {
if n, err := strconv.Atoi(num); err == nil {
return int64(n)
}
return 0
}
func (s *service) GetAllUpcomingEvents(ctx context.Context) ([]domain.BaseEvent, error) {
return s.store.GetAllUpcomingEvents(ctx) return s.store.GetAllUpcomingEvents(ctx)
} }
func (s *service) GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, error) { func (s *service) GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, error) {
return s.store.GetExpiredUpcomingEvents(ctx, filter) return s.store.GetExpiredUpcomingEvents(ctx, filter)
} }
func (s *service) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, int64, error) { func (s *service) GetPaginatedUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) {
return s.store.GetPaginatedUpcomingEvents(ctx, filter) return s.store.GetPaginatedUpcomingEvents(ctx, filter)
} }
func (s *service) GetUpcomingEventByID(ctx context.Context, ID string) (domain.UpcomingEvent, error) { func (s *service) GetUpcomingEventByID(ctx context.Context, ID string) (domain.BaseEvent, error) {
return s.store.GetUpcomingEventByID(ctx, ID) return s.store.GetUpcomingEventByID(ctx, ID)
} }
@ -517,48 +467,20 @@ func (s *service) UpdateEventStatus(ctx context.Context, eventID string, status
return s.store.UpdateEventStatus(ctx, eventID, status) return s.store.UpdateEventStatus(ctx, eventID, status)
} }
func (s *service) UpdateEventFeatured(ctx context.Context, eventID string, flagged bool) error {
return s.store.UpdateEventFeatured(ctx, eventID, flagged)
}
func (s *service) IsEventMonitored(ctx context.Context, eventID string) (bool, error) { func (s *service) IsEventMonitored(ctx context.Context, eventID string) (bool, error) {
return s.store.IsEventMonitored(ctx, eventID) return s.store.IsEventMonitored(ctx, eventID)
} }
func (s *service) UpdateEventMonitored(ctx context.Context, eventID string, IsMonitored bool) error { func (s *service) UpdateEventMonitored(ctx context.Context, eventID string, IsMonitored bool) error {
return s.store.UpdateEventFeatured(ctx, eventID, IsMonitored) return s.store.UpdateEventMonitored(ctx, eventID, IsMonitored)
} }
// func (s *service) GetAndStoreMatchResult(ctx context.Context, eventID string) error { func (s *service) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) {
// url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%s", s.token, eventID) return s.store.GetEventsWithSettings(ctx, companyID, filter)
}
// resp, err := http.Get(url) func (s *service) GetEventWithSettingByID(ctx context.Context, ID string, companyID int64) (domain.EventWithSettings, error) {
// if err != nil { return s.store.GetEventWithSettingByID(ctx, ID, companyID)
// return fmt.Errorf("failed to fetch result: %w", err) }
// } func (s *service) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error {
// defer resp.Body.Close() return s.store.UpdateEventSettings(ctx, event)
}
// body, _ := io.ReadAll(resp.Body)
// // Parse the API response
// var apiResp struct {
// Results []struct {
// ID string `json:"id"`
// Ss string `json:"ss"` // Full-time score
// Status string `json:"time_status"`
// } `json:"results"`
// }
// err = json.Unmarshal(body, &apiResp)
// if err != nil || len(apiResp.Results) == 0 {
// return fmt.Errorf("invalid response or no results found")
// }
// result := apiResp.Results[0]
// err = s.store.UpdateFinalScore(ctx, result.ID, result.Ss, result.Status)
// if err != nil {
// return fmt.Errorf("failed to update final score in database: %w", err)
// }
// return nil
// }

View File

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

View File

@ -17,20 +17,24 @@ func New(store *repository.Store) Service {
} }
} }
func (s *service) SaveLeague(ctx context.Context, l domain.League) error { func (s *service) SaveLeague(ctx context.Context, league domain.CreateLeague) error {
return s.store.SaveLeague(ctx, l) return s.store.SaveLeague(ctx, league)
} }
func (s *service) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error) { func (s *service) SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error {
return s.store.SaveLeagueSettings(ctx, leagueSettings)
}
func (s *service) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error) {
return s.store.GetAllLeagues(ctx, filter) return s.store.GetAllLeagues(ctx, filter)
} }
func (s *service) GetFeaturedLeagues(ctx context.Context) ([]domain.League, error) { func (s *service) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error) {
return s.store.GetFeaturedLeagues(ctx) return s.store.GetAllLeaguesByCompany(ctx, companyID, filter)
} }
func (s *service) SetLeagueActive(ctx context.Context, leagueId int64, isActive bool) error { func (s *service) CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) {
return s.store.SetLeagueActive(ctx, leagueId, isActive) return s.store.CheckLeagueSupport(ctx, leagueID, companyID)
} }
func (s *service) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error { func (s *service) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error {

View File

@ -0,0 +1,29 @@
package odds
// import (
// "context"
// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
// )
// func (s *ServiceImpl) InsertCustomOdds(ctx context.Context, odd domain.CreateCustomOdd) (domain.CustomOdd, error) {
// return s.store.InsertCustomOdds(ctx, odd)
// }
// func (s *ServiceImpl) GetAllCustomOdds(ctx context.Context, filter domain.CustomOddFilter) ([]domain.CustomOdd, error){
// return s.store.GetAllCustomOdds(ctx, filter)
// }
// func (s *ServiceImpl) GetCustomOddByID(ctx context.Context, id int64) (domain.CustomOdd, error){
// return s.store.GetCustomOddByID(ctx, id)
// }
// func (s *ServiceImpl) GetCustomOddByOddID(ctx context.Context, oddId int64, companyID int64) (domain.CustomOdd, error){
// return s.store.GetCustomOddByOddID(ctx, oddId, companyID)
// }
// func (s *ServiceImpl) DeleteCustomOddByID(ctx context.Context, id int64) error{
// return s.store.DeleteCustomOddByID(ctx, id)
// }
// func (s *ServiceImpl) DeleteCustomOddsByOddID(ctx context.Context, oddId int64, companyID int64) error{
// return s.store.DeleteCustomOddsByOddID(ctx, oddId, companyID)
// }
// func (s *ServiceImpl) DeleteCustomOddByEventID(ctx context.Context, eventID string) error{
// return s.store.DeleteCustomOddByEventID(ctx, eventID)
// }

View File

@ -0,0 +1,28 @@
package odds
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
func (s *ServiceImpl) InsertDisabledOdd(ctx context.Context, odd domain.CreateDisabledOdd) (domain.DisabledOdd, error) {
return s.store.InsertDisabledOdd(ctx, odd)
}
func (s *ServiceImpl) GetAllDisabledOdds(ctx context.Context) ([]domain.DisabledOdd, error) {
return s.store.GetAllDisabledOdds(ctx)
}
func (s *ServiceImpl) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (domain.DisabledOdd, error) {
return s.store.GetDisabledOddByRawOddID(ctx, rawOddID)
}
func (s *ServiceImpl) GetDisabledOddByID(ctx context.Context, id int64) (domain.DisabledOdd, error) {
return s.store.GetDisabledOddByID(ctx, id)
}
func (s *ServiceImpl) DeleteDisabledOddsByID(ctx context.Context, id int64) error {
return s.store.DeleteDisabledOddsByID(ctx, id)
}
func (s *ServiceImpl) DeleteDisabledOddsByRawOddID(ctx context.Context, id int64) error {
return s.store.DeleteDisabledOddsByRawOddID(ctx, id)
}

View File

@ -0,0 +1,15 @@
package odds
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
func (s *ServiceImpl) GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) {
return s.store.GetAllOddHistory(ctx, filter)
}
func (s *ServiceImpl) GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) {
return s.store.GetInitialOddPerDay(ctx, filter)
}

View File

@ -11,13 +11,32 @@ type Service interface {
FetchNonLiveOdds(ctx context.Context) error FetchNonLiveOdds(ctx context.Context) error
FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error) FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error)
ParseOddSections(ctx context.Context, res json.RawMessage, sportID int64) (domain.ParseOddSectionsRes, error) ParseOddSections(ctx context.Context, res json.RawMessage, sportID int64) (domain.ParseOddSectionsRes, error)
GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.OddMarket, error)
GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.Odd, error) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.OddMarket, error)
GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit domain.ValidInt64, offset domain.ValidInt64) ([]domain.Odd, error) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit domain.ValidInt64, offset domain.ValidInt64) ([]domain.OddMarket, error)
GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) GetALLPrematchOdds(ctx context.Context) ([]domain.OddMarket, error)
GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.RawOddsByMarketID, error) GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.RawOddsByMarketID, error)
DeleteOddsForEvent(ctx context.Context, eventID string) error DeleteOddsForEvent(ctx context.Context, eventID string) error
// Odd History
InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error) InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error)
GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error)
GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error)
// Disabling Odds
InsertDisabledOdd(ctx context.Context, odd domain.CreateDisabledOdd) (domain.DisabledOdd, error)
GetAllDisabledOdds(ctx context.Context) ([]domain.DisabledOdd, error)
GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (domain.DisabledOdd, error)
GetDisabledOddByID(ctx context.Context, id int64) (domain.DisabledOdd, error)
DeleteDisabledOddsByID(ctx context.Context, id int64) error
DeleteDisabledOddsByRawOddID(ctx context.Context, id int64) error
// Custom Odds
// InsertCustomOdds(ctx context.Context, odd domain.CreateCustomOdd) (domain.CustomOdd, error)
// GetAllCustomOdds(ctx context.Context, filter domain.CustomOddFilter) ([]domain.CustomOdd, error)
// GetCustomOddByID(ctx context.Context, id int64) (domain.CustomOdd, error)
// GetCustomOddByOddID(ctx context.Context, oddId int64, companyID int64) (domain.CustomOdd, error)
// DeleteCustomOddByID(ctx context.Context, id int64) error
// DeleteCustomOddsByOddID(ctx context.Context, oddId int64, companyID int64) error
// DeleteCustomOddByEventID(ctx context.Context, eventID string) error
} }

View File

@ -164,115 +164,117 @@ func (s *ServiceImpl) fetchBet365Odds(ctx context.Context) error {
return nil return nil
} }
func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error { // func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error {
// getting odds for a specific event is not possible for bwin, most specific we can get is fetch odds on a single sport // // getting odds for a specific event is not possible for bwin, most specific we can get is fetch odds on a single sport
// so instead of having event and odds fetched separetly event will also be fetched along with the odds // // so instead of having event and odds fetched separetly event will also be fetched along with the odds
sportIds := []int{4, 12, 7} // sportIds := []int{4, 12, 7}
for _, sportId := range sportIds { // for _, sportId := range sportIds {
url := fmt.Sprintf("https://api.b365api.com/v1/bwin/prematch?sport_id=%d&token=%s", sportId, s.config.Bet365Token) // url := fmt.Sprintf("https://api.b365api.com/v1/bwin/prematch?sport_id=%d&token=%s", sportId, s.config.Bet365Token)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) // req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { // if err != nil {
s.mongoLogger.Error( // s.mongoLogger.Error(
"Failed to create request for sportId", // "Failed to create request for sportId",
zap.Int("sportID", sportId), // zap.Int("sportID", sportId),
zap.Error(err), // zap.Error(err),
) // )
continue // continue
} // }
resp, err := s.client.Do(req) // resp, err := s.client.Do(req)
if err != nil { // if err != nil {
s.mongoLogger.Error( // s.mongoLogger.Error(
"Failed to fetch request for sportId", // "Failed to fetch request for sportId",
zap.Int("sportID", sportId), // zap.Int("sportID", sportId),
zap.Error(err), // zap.Error(err),
) // )
continue // continue
} // }
defer resp.Body.Close() // defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) // body, err := io.ReadAll(resp.Body)
if err != nil { // if err != nil {
s.mongoLogger.Error( // s.mongoLogger.Error(
"Failed to read response body for sportId", // "Failed to read response body for sportId",
zap.Int("sportID", sportId), // zap.Int("sportID", sportId),
zap.Error(err), // zap.Error(err),
) // )
continue // continue
} // }
var data struct { // var data struct {
Success int `json:"success"` // Success int `json:"success"`
Results []map[string]interface{} `json:"results"` // Results []map[string]interface{} `json:"results"`
} // }
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { // if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
fmt.Printf("Decode failed for sport_id=%d\nRaw: %s\n", sportId, string(body)) // fmt.Printf("Decode failed for sport_id=%d\nRaw: %s\n", sportId, string(body))
s.mongoLogger.Error( // s.mongoLogger.Error(
"Failed to decode BWin response body", // "Failed to decode BWin response body",
zap.Int("sportID", sportId), // zap.Int("sportID", sportId),
zap.Error(err), // zap.Error(err),
) // )
continue // continue
} // }
for _, res := range data.Results { // for _, res := range data.Results {
if getInt(res["Id"]) == -1 { // if getInt(res["Id"]) == -1 {
continue // continue
} // }
event := domain.Event{ // event := domain.CreateEvent{
ID: strconv.Itoa(getInt(res["Id"])), // ID: strconv.Itoa(getInt(res["Id"])),
SportID: int32(getInt(res["SportId"])), // SportID: int32(getInt(res["SportId"])),
LeagueID: int32(getInt(res["LeagueId"])), // LeagueID: int64(getInt(res["LeagueId"])),
LeagueName: getString(res["Leaguename"]), // LeagueName: getString(res["Leaguename"]),
HomeTeam: getString(res["HomeTeam"]), // HomeTeam: getString(res["HomeTeam"]),
HomeTeamID: int32(getInt(res["HomeTeamId"])), // HomeTeamID: int64(getInt(res["HomeTeamId"])),
AwayTeam: getString(res["AwayTeam"]), // AwayTeam: getString(res["AwayTeam"]),
AwayTeamID: int32(getInt(res["AwayTeamId"])), // AwayTeamID: int64(getInt(res["AwayTeamId"])),
StartTime: time.Now().UTC().Format(time.RFC3339), // StartTime: time.Now().UTC(),
TimerStatus: "1", // IsLive: true,
IsLive: true, // Status: domain.STATUS_IN_PLAY,
Status: "live", // Source: domain.EVENT_SOURCE_BWIN,
Source: "bwin", // MatchName: "",
} // HomeTeamImage: "",
// AwayTeamImage: "",
// }
if err := s.store.SaveEvent(ctx, event); err != nil { // if err := s.store.SaveEvent(ctx, event); err != nil {
fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) // fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err)
s.mongoLogger.Error( // s.mongoLogger.Error(
"Could not store live event", // "Could not store live event",
zap.Int("sportID", sportId), // zap.Int("sportID", sportId),
zap.String("eventID", event.ID), // zap.String("eventID", event.ID),
zap.Error(err), // zap.Error(err),
) // )
continue // continue
} // }
for _, market := range []string{"Markets, optionMarkets"} { // for _, market := range []string{"Markets, optionMarkets"} {
for _, m := range getMapArray(res[market]) { // for _, m := range getMapArray(res[market]) {
name := getMap(m["name"]) // name := getMap(m["name"])
marketName := getString(name["value"]) // marketName := getString(name["value"])
market := domain.Market{ // market := domain.CreateOddMarket{
EventID: event.ID, // EventID: event.ID,
MarketID: getString(m["id"]), // MarketID: getString(m["id"]),
MarketCategory: getString(m["category"]), // MarketCategory: getString(m["category"]),
MarketName: marketName, // MarketName: marketName,
Source: "bwin",
}
results := getMapArray(m["results"]) // }
market.Odds = results
s.store.SaveNonLiveMarket(ctx, market) // results := getMapArray(m["results"])
// market.Odds = results
} // s.store.SaveOddMarket(ctx, market)
}
}
} // }
return nil // }
} // }
// }
// return nil
// }
func (s *ServiceImpl) FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error) { func (s *ServiceImpl) FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error) {
@ -545,7 +547,7 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName
} }
// Check if the market id is a string // Check if the market id is a string
marketIDint := market.ID.Int64 marketIDint := market.ID.Value
// if err != nil { // if err != nil {
// s.mongoLogger.Error( // s.mongoLogger.Error(
// "Invalid market id", // "Invalid market id",
@ -579,9 +581,8 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName
continue continue
} }
marketRecord := domain.Market{ marketRecord := domain.CreateOddMarket{
EventID: eventID, EventID: eventID,
FI: fi,
MarketCategory: sectionName, MarketCategory: sectionName,
MarketType: marketType, MarketType: marketType,
MarketName: market.Name, MarketName: market.Name,
@ -589,7 +590,6 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
Odds: marketOdds, Odds: marketOdds,
// bwin won't reach this code so bet365 is hardcoded for now // bwin won't reach this code so bet365 is hardcoded for now
Source: "bet365",
} }
if err := s.CheckAndInsertOddHistory(ctx, marketRecord); err != nil { if err := s.CheckAndInsertOddHistory(ctx, marketRecord); err != nil {
@ -603,7 +603,7 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName
continue continue
} }
err = s.store.SaveNonLiveMarket(ctx, marketRecord) err = s.store.SaveOddMarket(ctx, marketRecord)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error(
"failed to save market", "failed to save market",
@ -623,57 +623,38 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName
return nil return nil
} }
func (s *ServiceImpl) CheckAndInsertOddHistory(ctx context.Context, market domain.Market) error { func (s *ServiceImpl) CheckAndInsertOddHistory(ctx context.Context, market domain.CreateOddMarket) error {
isEventMonitored, err := s.eventSvc.IsEventMonitored(ctx, market.EventID) isEventMonitored, err := s.eventSvc.IsEventMonitored(ctx, market.EventID)
if err != nil { marketLogger := s.mongoLogger.With(
s.mongoLogger.Error(
"failed to get is_monitored",
zap.String("market_id", market.MarketID), zap.String("market_id", market.MarketID),
zap.String("market_name", market.Name), zap.String("market_name", market.MarketName),
zap.String("eventID", market.EventID), zap.String("eventID", market.EventID),
zap.Error(err),
) )
if err != nil {
marketLogger.Error("failed to get is_monitored", zap.Error(err))
} }
if !isEventMonitored { if !isEventMonitored {
return nil return nil
} }
oldOdds, err := s.store.GetRawOddsByMarketID(ctx, market.MarketID, market.EventID) oldOdds, err := s.store.GetOddsByMarketID(ctx, market.MarketID, market.EventID)
if err != nil { if err != nil {
s.mongoLogger.Error( marketLogger.Error("failed to get raw odds by market id", zap.Error(err))
"failed to get raw odds by market id",
zap.String("market_id", market.MarketID),
zap.String("market_name", market.Name),
zap.String("eventID", market.EventID),
zap.Error(err),
)
return err return err
} }
if len(oldOdds.RawOdds) != len(market.Odds) { if len(oldOdds.RawOdds) != len(market.Odds) {
s.mongoLogger.Error( marketLogger.Error("new odds data does not match old odds data", zap.Error(err))
"new odds data does not match old odds data",
zap.String("market_id", market.MarketID),
zap.String("market_name", market.Name),
zap.String("eventID", market.EventID),
zap.Error(err),
)
return fmt.Errorf("new odds data does not match old odds data") return fmt.Errorf("new odds data does not match old odds data")
} }
oldRawOdds, err := convertRawMessage(oldOdds.RawOdds) oldRawOdds, err := convertRawMessage(oldOdds.RawOdds)
if err != nil { if err != nil {
s.mongoLogger.Error( marketLogger.Error("failed to convert raw odds to map", zap.Error(err))
"failed to convert raw odds to map",
zap.String("market_id", market.MarketID),
zap.String("market_name", market.Name),
zap.String("eventID", market.EventID),
zap.Error(err),
)
return err return err
} }
@ -698,7 +679,7 @@ func (s *ServiceImpl) CheckAndInsertOddHistory(ctx context.Context, market domai
s.mongoLogger.Error( s.mongoLogger.Error(
"failed to insert odd history", "failed to insert odd history",
zap.String("market_id", market.MarketID), zap.String("market_id", market.MarketID),
zap.String("market_name", market.Name), zap.String("market_name", market.MarketName),
zap.String("eventID", market.EventID), zap.String("eventID", market.EventID),
zap.Int64("odd_id", oldOdds.ID), zap.Int64("odd_id", oldOdds.ID),
zap.Int("raw_odd_id", newRawOddID), zap.Int("raw_odd_id", newRawOddID),
@ -716,29 +697,37 @@ func (s *ServiceImpl) CheckAndInsertOddHistory(ctx context.Context, market domai
return nil return nil
} }
func (s *ServiceImpl) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { func (s *ServiceImpl) GetAllOdds(ctx context.Context, filter domain.OddMarketFilter) ([]domain.OddMarket, error) {
return s.store.GetPrematchOdds(ctx, eventID) return s.store.GetAllOdds(ctx, filter)
} }
func (s *ServiceImpl) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) { func (s *ServiceImpl) GetAllOddsWithSettings(ctx context.Context, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) {
return s.store.GetALLPrematchOdds(ctx) return s.store.GetAllOddsWithSettings(ctx, companyID, filter)
} }
func (s *ServiceImpl) GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.RawOddsByMarketID, error) { func (s *ServiceImpl) GetOddsByMarketID(ctx context.Context, marketID string, eventID string) (domain.OddMarket, error) {
rows, err := s.store.GetRawOddsByMarketID(ctx, marketID, upcomingID) rows, err := s.store.GetOddsByMarketID(ctx, marketID, eventID)
if err != nil { if err != nil {
return domain.RawOddsByMarketID{}, err return domain.OddMarket{}, err
}
return rows, nil
}
func (s *ServiceImpl) GetOddsWithSettingsByMarketID(ctx context.Context, marketID string, eventID string, companyID int64) (domain.OddMarketWithSettings, error) {
rows, err := s.store.GetOddsWithSettingsByMarketID(ctx, marketID, eventID, companyID)
if err != nil {
return domain.OddMarketWithSettings{}, err
} }
return rows, nil return rows, nil
} }
func (s *ServiceImpl) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.Odd, error) { func (s *ServiceImpl) GetOddsByEventID(ctx context.Context, upcomingID string, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) {
return s.store.GetPrematchOddsByUpcomingID(ctx, upcomingID) return s.store.GetOddsByEventID(ctx, upcomingID, filter)
} }
func (s *ServiceImpl) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit, offset domain.ValidInt32) ([]domain.Odd, error) { func (s *ServiceImpl) GetOddsWithSettingsByEventID(ctx context.Context, upcomingID string, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) {
return s.store.GetPaginatedPrematchOddsByUpcomingID(ctx, upcomingID, limit, offset) return s.store.GetOddsWithSettingsByEventID(ctx, upcomingID, companyID, filter)
} }
func (s *ServiceImpl) DeleteOddsForEvent(ctx context.Context, eventID string) error { func (s *ServiceImpl) DeleteOddsForEvent(ctx context.Context, eventID string) error {

View File

@ -70,89 +70,57 @@ var (
func (s *Service) UpdateResultForOutcomes(ctx context.Context, eventID int64, resultRes json.RawMessage, sportID int64) error { func (s *Service) UpdateResultForOutcomes(ctx context.Context, eventID int64, resultRes json.RawMessage, sportID int64) error {
// TODO: Optimize this since there could be many outcomes with the same event_id and market_id that could be updated the same time // TODO: Optimize this since there could be many outcomes with the same event_id and market_id that could be updated the same time
outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID, true) outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID, true)
if err != nil { logger := s.mongoLogger.With(
s.mongoLogger.Error(
"Failed to get pending bet outcomes",
zap.Int64("eventID", eventID), zap.Int64("eventID", eventID),
zap.Error(err), zap.Int64("sportID", sportID),
) )
if err != nil {
logger.Error("Failed to get pending bet outcomes", zap.Error(err))
return fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err) return fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err)
} }
// if len(outcomes) == 0 {
// s.mongoLogger.Info(
// "No bets have been placed on event",
// zap.Int64("eventID", eventID),
// )
// }
for _, outcome := range outcomes { for _, outcome := range outcomes {
if outcome.Expires.After(time.Now()) { outcomeLogger := logger.With(
s.mongoLogger.Warn(
"Outcome is not expired yet",
zap.Int64("eventID", eventID),
zap.Int64("outcome_id", outcome.ID), zap.Int64("outcome_id", outcome.ID),
zap.Error(err), zap.Int32("outcome_status", int32(outcome.Status)),
zap.Int64("outcome_bet_id", outcome.BetID),
zap.String("outcome_market_id", outcome.MarketName),
) )
if outcome.Expires.After(time.Now()) {
outcomeLogger.Warn("Outcome is not expired yet", zap.Error(err))
return fmt.Errorf("Outcome has not expired yet") return fmt.Errorf("Outcome has not expired yet")
} }
parseResult, err := s.parseResult(resultRes, outcome, sportID) parseResult, err := s.parseResult(resultRes, outcome, sportID)
if err != nil { if err != nil {
s.mongoLogger.Error( outcomeLogger.Error("Failed to parse result", zap.Error(err))
"Failed to parse result",
zap.Int64("eventID", eventID),
zap.Int64("outcome_id", outcome.ID),
zap.Error(err),
)
return err return err
} }
outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, parseResult.Status) outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, parseResult.Status)
if err != nil { if err != nil {
s.mongoLogger.Error( outcomeLogger.Error("Failed to update bet outcome status", zap.Error(err))
"Failed to update bet outcome status",
zap.Int64("eventID", eventID),
zap.Int64("outcome_id", outcome.ID),
zap.Error(err),
)
return err return err
} }
if outcome.Status == domain.OUTCOME_STATUS_ERROR || outcome.Status == domain.OUTCOME_STATUS_PENDING { if outcome.Status == domain.OUTCOME_STATUS_ERROR || outcome.Status == domain.OUTCOME_STATUS_PENDING {
s.mongoLogger.Error( outcomeLogger.Error("Outcome has been updated to pending or error", zap.Error(err))
"Outcome has been updated to pending or error",
zap.Int64("eventID", eventID),
zap.Error(err),
)
return fmt.Errorf("Error while updating outcome") return fmt.Errorf("Error while updating outcome")
} }
status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID) status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID)
if err != nil { if err != nil {
if err != bet.ErrOutcomesNotCompleted { if err != bet.ErrOutcomesNotCompleted {
s.mongoLogger.Error( outcomeLogger.Error("Failed to check bet outcome for bet", zap.Error(err))
"Failed to check bet outcome for bet",
zap.Int64("eventID", eventID),
zap.Int64("betID", outcome.BetID),
zap.Error(err),
)
} }
return err return err
} }
s.mongoLogger.Info( outcomeLogger.Info("Updating bet status", zap.String("status", status.String()))
"Updating bet status",
zap.Int64("eventID", eventID),
zap.Int64("betID", outcome.BetID),
zap.String("status", status.String()),
zap.Int64("eventID", eventID),
)
err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status) err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status)
if err != nil { if err != nil {
s.mongoLogger.Error( outcomeLogger.Error("Failed to update bet status", zap.Error(err))
"Failed to update bet status",
zap.Int64("eventID", eventID),
zap.Error(err),
)
return err return err
} }
} }
@ -268,46 +236,32 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
var resultLog domain.CreateResultLog var resultLog domain.CreateResultLog
var resultStatusBets domain.ResultStatusBets var resultStatusBets domain.ResultStatusBets
for _, event := range events { for _, event := range events {
eventLogger := s.mongoLogger.With(
zap.String("eventID", event.ID),
)
eventID, err := strconv.ParseInt(event.ID, 10, 64) eventID, err := strconv.ParseInt(event.ID, 10, 64)
if err != nil { if err != nil {
s.mongoLogger.Error( eventLogger.Error("Failed to parse Event ID", zap.Error(err))
"Failed to parse Event ID",
zap.String("eventID", event.ID),
zap.Error(err),
)
continue continue
} }
result, err := s.fetchResult(ctx, eventID) result, err := s.fetchResult(ctx, eventID)
if err != nil { if err != nil {
if err == ErrEventIsNotActive { if err == ErrEventIsNotActive {
s.mongoLogger.Warn( eventLogger.Warn("Event is not active", zap.Error(err))
"Event is not active",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
s.mongoLogger.Error( eventLogger.Error("Failed to fetch result", zap.Error(err))
"Failed to fetch result",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
var commonResp domain.CommonResultResponse var commonResp domain.CommonResultResponse
if err := json.Unmarshal(result.Results[0], &commonResp); err != nil { if err := json.Unmarshal(result.Results[0], &commonResp); err != nil {
s.mongoLogger.Error( eventLogger.Error("Failed to unmarshal common result", zap.Error(err))
"Failed to unmarshal common result",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
timeStatusParsed := commonResp.TimeStatus.Int64 timeStatusParsed := commonResp.TimeStatus.Value
// if err != nil { // if err != nil {
// s.mongoLogger.Error( // s.mongoLogger.Error(
// "Failed to parse time status", // "Failed to parse time status",
@ -325,6 +279,10 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
// continue // continue
// } // }
// Admin users will be able to review the events // Admin users will be able to review the events
commonRespLogger := eventLogger.With(
zap.Int64("parsed_time_status", timeStatusParsed),
zap.String("response_sport_id", commonResp.SportID),
)
switch timeStatusParsed { switch timeStatusParsed {
case int64(domain.TIME_STATUS_NOT_STARTED), int64(domain.TIME_STATUS_IN_PLAY): case int64(domain.TIME_STATUS_NOT_STARTED), int64(domain.TIME_STATUS_IN_PLAY):
resultLog.StatusNotFinishedCount += 1 resultLog.StatusNotFinishedCount += 1
@ -342,20 +300,12 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
err = s.repo.DeleteEvent(ctx, event.ID) err = s.repo.DeleteEvent(ctx, event.ID)
if err != nil { if err != nil {
s.mongoLogger.Error( commonRespLogger.Error("Failed to remove event", zap.Error(err))
"Failed to remove event",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
err = s.repo.DeleteOddsForEvent(ctx, event.ID) err = s.repo.DeleteOddsForEvent(ctx, event.ID)
if err != nil { if err != nil {
s.mongoLogger.Error( commonRespLogger.Error("Failed to remove odds for event", zap.Error(err))
"Failed to remove odds for event",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
resultLog.RemovedCount += 1 resultLog.RemovedCount += 1
@ -393,37 +343,21 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
} }
sportID, err := strconv.ParseInt(commonResp.SportID, 10, 64) sportID, err := strconv.ParseInt(commonResp.SportID, 10, 64)
if err != nil { if err != nil {
s.mongoLogger.Error( commonRespLogger.Error("Failed to parse sport id", zap.Error(err))
"Failed to parse sport id",
zap.String("sportID", commonResp.SportID),
zap.Error(err),
)
continue continue
} }
err = s.UpdateResultForOutcomes(ctx, eventID, result.Results[0], sportID) err = s.UpdateResultForOutcomes(ctx, eventID, result.Results[0], sportID)
if err != nil { if err != nil {
s.mongoLogger.Error( commonRespLogger.Error("Error while updating result for event", zap.Error(err))
"Error while updating result for event",
zap.Int64("eventID", eventID),
zap.Error(err),
)
} }
err = s.repo.DeleteEvent(ctx, event.ID) err = s.repo.DeleteEvent(ctx, event.ID)
if err != nil { if err != nil {
s.mongoLogger.Error( commonRespLogger.Error("Failed to remove event", zap.Error(err))
"Failed to remove event",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
err = s.repo.DeleteOddsForEvent(ctx, event.ID) err = s.repo.DeleteOddsForEvent(ctx, event.ID)
if err != nil { if err != nil {
s.mongoLogger.Error( commonRespLogger.Error("Failed to remove odds for event", zap.Error(err))
"Failed to remove odds for event",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
resultLog.RemovedCount += 1 resultLog.RemovedCount += 1
@ -452,20 +386,12 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
err = s.repo.DeleteEvent(ctx, event.ID) err = s.repo.DeleteEvent(ctx, event.ID)
if err != nil { if err != nil {
s.mongoLogger.Error( commonRespLogger.Error("Failed to remove event", zap.Error(err))
"Failed to remove event",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
err = s.repo.DeleteOddsForEvent(ctx, event.ID) err = s.repo.DeleteOddsForEvent(ctx, event.ID)
if err != nil { if err != nil {
s.mongoLogger.Error( commonRespLogger.Error("Failed to remove odds for event", zap.Error(err))
"Failed to remove odds for event",
zap.Int64("eventID", eventID),
zap.Error(err),
)
continue continue
} }
resultLog.RemovedCount += 1 resultLog.RemovedCount += 1
@ -718,7 +644,7 @@ func (s *Service) CheckAndUpdateExpiredEvents(ctx context.Context) (int64, error
var eventStatus domain.EventStatus var eventStatus domain.EventStatus
// TODO Change event status to int64 enum // TODO Change event status to int64 enum
timeStatus := commonResp.TimeStatus.Int64 timeStatus := commonResp.TimeStatus.Value
// if err != nil { // if err != nil {
// s.mongoLogger.Error( // s.mongoLogger.Error(
// "Invalid time status", // "Invalid time status",
@ -958,7 +884,7 @@ func (s *Service) GetResultsForEvent(ctx context.Context, eventID string) (json.
outcomes := make([]domain.BetOutcome, 0) outcomes := make([]domain.BetOutcome, 0)
for _, section := range parsedOddSections.Sections { for _, section := range parsedOddSections.Sections {
for _, market := range section.Sp { for _, market := range section.Sp {
marketIDint := market.ID.Int64 marketIDint := market.ID.Value
// if err != nil { // if err != nil {
// s.mongoLogger.Error( // s.mongoLogger.Error(
// "Invalid market id", // "Invalid market id",
@ -1135,147 +1061,80 @@ func (s *Service) parseResult(resultResp json.RawMessage, outcome domain.BetOutc
var result domain.CreateResult var result domain.CreateResult
var err error var err error
logFields := []zap.Field{
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
}
switch sportID { switch sportID {
case domain.FOOTBALL: case domain.FOOTBALL:
result, err = s.parseFootball(resultResp, outcome) result, err = s.parseFootball(resultResp, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse football", append(logFields, zap.Error(err))...)
"Failed to parse football",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.BASKETBALL: case domain.BASKETBALL:
result, err = s.parseBasketball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseBasketball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse basketball", append(logFields, zap.Error(err))...)
"Failed to parse basketball",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.ICE_HOCKEY: case domain.ICE_HOCKEY:
result, err = s.parseIceHockey(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseIceHockey(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse ice hockey", append(logFields, zap.Error(err))...)
"Failed to parse ice hockey",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.CRICKET: case domain.CRICKET:
result, err = s.parseCricket(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseCricket(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse cricket", append(logFields, zap.Error(err))...)
"Failed to parse cricket",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.VOLLEYBALL: case domain.VOLLEYBALL:
result, err = s.parseVolleyball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseVolleyball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse volleyball", append(logFields, zap.Error(err))...)
"Failed to parse volleyball",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.DARTS: case domain.DARTS:
result, err = s.parseDarts(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseDarts(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse darts", append(logFields, zap.Error(err))...)
"Failed to parse darts",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.FUTSAL: case domain.FUTSAL:
result, err = s.parseFutsal(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseFutsal(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse futsal", append(logFields, zap.Error(err))...)
"Failed to parse futsal",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.AMERICAN_FOOTBALL: case domain.AMERICAN_FOOTBALL:
result, err = s.parseNFL(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseNFL(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse american football", append(logFields, zap.Error(err))...)
"Failed to parse american football",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.RUGBY_UNION: case domain.RUGBY_UNION:
result, err = s.parseRugbyUnion(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseRugbyUnion(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse rugby_union", append(logFields, zap.Error(err))...)
"Failed to parse rugby_union",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.RUGBY_LEAGUE: case domain.RUGBY_LEAGUE:
result, err = s.parseRugbyLeague(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseRugbyLeague(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse rugby_league", append(logFields, zap.Error(err))...)
"Failed to parse rugby_league",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
case domain.BASEBALL: case domain.BASEBALL:
result, err = s.parseBaseball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) result, err = s.parseBaseball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
if err != nil { if err != nil {
s.mongoLogger.Error( s.mongoLogger.Error("Failed to parse baseball", append(logFields, zap.Error(err))...)
"Failed to parse baseball",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
default: default:
s.mongoLogger.Error( s.mongoLogger.Error("Unsupported sport", append(logFields, zap.Error(err))...)
"Unsupported sport",
zap.Int64("event id", outcome.EventID),
zap.Int64("market_id", outcome.MarketID),
zap.Int64("sport_id", sportID),
zap.Error(err),
)
return domain.CreateResult{}, fmt.Errorf("unsupported sport: %v", sportID) return domain.CreateResult{}, fmt.Errorf("unsupported sport: %v", sportID)
} }

View File

@ -19,9 +19,9 @@ type UserStore interface {
UpdateUserCompany(ctx context.Context, id int64, companyID int64) error UpdateUserCompany(ctx context.Context, id int64, companyID int64) error
UpdateUserSuspend(ctx context.Context, id int64, status bool) error UpdateUserSuspend(ctx context.Context, id int64, status bool) error
DeleteUser(ctx context.Context, id int64) error DeleteUser(ctx context.Context, id int64) error
CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error)
GetUserByEmail(ctx context.Context, email string) (domain.User, error) GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error)
GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error) GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error)
SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error)
UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64) error // identifier verified email or phone UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64) error // identifier verified email or phone

View File

@ -7,17 +7,17 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
) )
func (s *Service) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) { // email,phone,error func (s *Service) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error) { // email,phone,error
return s.userStore.CheckPhoneEmailExist(ctx, phoneNum, email) return s.userStore.CheckPhoneEmailExist(ctx, phoneNum, email, companyID)
} }
func (s *Service) SendRegisterCode(ctx context.Context, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider) error { func (s *Service) SendRegisterCode(ctx context.Context, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider, companyID domain.ValidInt64) error {
var err error var err error
// check if user exists // check if user exists
switch medium { switch medium {
case domain.OtpMediumEmail: case domain.OtpMediumEmail:
_, err = s.userStore.GetUserByEmail(ctx, sentTo) _, err = s.userStore.GetUserByEmail(ctx, sentTo, companyID)
case domain.OtpMediumSms: case domain.OtpMediumSms:
_, err = s.userStore.GetUserByPhone(ctx, sentTo) _, err = s.userStore.GetUserByPhone(ctx, sentTo, companyID)
} }
if err != nil && err != domain.ErrUserNotFound { if err != nil && err != domain.ErrUserNotFound {
@ -68,6 +68,7 @@ func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterU
Role: domain.RoleCustomer, Role: domain.RoleCustomer,
EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail, EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail,
PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms, PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms,
CompanyID: registerReq.CompanyID,
} }
// create the user and mark otp as used // create the user and mark otp as used
user, err := s.userStore.CreateUser(ctx, userR, otp.ID, false) user, err := s.userStore.CreateUser(ctx, userR, otp.ID, false)

View File

@ -8,15 +8,15 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
) )
func (s *Service) SendResetCode(ctx context.Context, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider) error { func (s *Service) SendResetCode(ctx context.Context, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider, companyID domain.ValidInt64) error {
var err error var err error
// check if user exists // check if user exists
switch medium { switch medium {
case domain.OtpMediumEmail: case domain.OtpMediumEmail:
_, err = s.userStore.GetUserByEmail(ctx, sentTo) _, err = s.userStore.GetUserByEmail(ctx, sentTo, companyID)
case domain.OtpMediumSms: case domain.OtpMediumSms:
_, err = s.userStore.GetUserByPhone(ctx, sentTo) _, err = s.userStore.GetUserByPhone(ctx, sentTo, companyID)
} }
if err != nil { if err != nil {

View File

@ -38,8 +38,13 @@ type loginCustomerRes struct {
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse // @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/auth/customer-login [post] // @Router /api/v1/{tenant_slug}/customer-login [post]
func (h *Handler) LoginCustomer(c *fiber.Ctx) error { func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req loginCustomerReq var req loginCustomerReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse LoginCustomer request", h.mongoLoggerSvc.Info("Failed to parse LoginCustomer request",
@ -58,7 +63,8 @@ func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password) successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password, companyID)
if err != nil { if err != nil {
switch { switch {
case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound): case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound):
@ -153,8 +159,13 @@ type loginAdminRes struct {
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse // @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/auth/admin-login [post] // @Router /api/v1/{tenant_slug}/admin-login [post]
func (h *Handler) LoginAdmin(c *fiber.Ctx) error { func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req loginAdminReq var req loginAdminReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse LoginAdmin request", h.mongoLoggerSvc.Info("Failed to parse LoginAdmin request",
@ -173,7 +184,7 @@ func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password) successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password, companyID)
if err != nil { if err != nil {
switch { switch {
case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound): case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound):
@ -243,6 +254,107 @@ func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Login successful", res, nil) return response.WriteJSON(c, fiber.StatusOK, "Login successful", res, nil)
} }
// LoginSuper godoc
// @Summary Login super-admin
// @Description Login super-admin
// @Tags auth
// @Accept json
// @Produce json
// @Param login body loginAdminReq true "Login super-admin"
// @Success 200 {object} loginAdminRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/super-login [post]
func (h *Handler) LoginSuper(c *fiber.Ctx) error {
var req loginAdminReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse LoginAdmin request",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body"+err.Error())
}
if valErrs, ok := h.validator.Validate(c, req); !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password, domain.ValidInt64{})
if err != nil {
switch {
case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound):
h.mongoLoggerSvc.Info("Login attempt failed: Invalid credentials",
zap.Int("status_code", fiber.StatusUnauthorized),
zap.String("email", req.Email),
zap.String("phone", req.PhoneNumber),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusUnauthorized, "Invalid credentials")
case errors.Is(err, authentication.ErrUserSuspended):
h.mongoLoggerSvc.Info("Login attempt failed: User login has been locked",
zap.Int("status_code", fiber.StatusUnauthorized),
zap.String("email", req.Email),
zap.String("phone", req.PhoneNumber),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusUnauthorized, "User login has been locked")
default:
h.mongoLoggerSvc.Error("Login failed",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Internal server error")
}
}
if successRes.Role != domain.RoleSuperAdmin {
h.mongoLoggerSvc.Warn("Login attempt: super-admin login of non-super-admin",
zap.Int("status_code", fiber.StatusForbidden),
zap.String("role", string(successRes.Role)),
zap.String("email", req.Email),
zap.String("phone", req.PhoneNumber),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusForbidden, "Only admin roles are allowed")
}
accessToken, err := jwtutil.CreateJwt(successRes.UserId, successRes.Role, successRes.CompanyID, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
if err != nil {
h.mongoLoggerSvc.Error("Failed to create access token",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Int64("user_id", successRes.UserId),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to generate access token")
}
res := loginCustomerRes{
AccessToken: accessToken,
RefreshToken: successRes.RfToken,
Role: string(successRes.Role),
}
h.mongoLoggerSvc.Info("Login successful",
zap.Int("status_code", fiber.StatusOK),
zap.Int64("user_id", successRes.UserId),
zap.String("role", string(successRes.Role)),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusOK, "Login successful", res, nil)
}
type refreshToken struct { type refreshToken struct {
AccessToken string `json:"access_token" validate:"required" example:"<jwt-token>"` AccessToken string `json:"access_token" validate:"required" example:"<jwt-token>"`
RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"` RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"`

View File

@ -29,30 +29,28 @@ import (
func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error { func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
page := c.QueryInt("page", 1) page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 10) pageSize := c.QueryInt("page_size", 10)
limit := domain.ValidInt64{ limit := domain.ValidInt32{
Value: int64(pageSize), Value: int32(pageSize),
Valid: true, Valid: true,
} }
offset := domain.ValidInt64{ offset := domain.ValidInt32{
Value: int64(page - 1), Value: int32(page - 1),
Valid: true, Valid: true,
} }
leagueIDQuery := c.Query("league_id") leagueIDQuery := c.Query("league_id")
var leagueID domain.ValidInt32 var leagueID domain.ValidInt64
if leagueIDQuery != "" { if leagueIDQuery != "" {
leagueIDInt, err := strconv.Atoi(leagueIDQuery) leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("invalid league id", domain.BadRequestLogger.Error("invalid league id",
zap.String("league_id", leagueIDQuery), zap.String("league_id", leagueIDQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, "invalid league id") return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
} }
leagueID = domain.ValidInt32{ leagueID = domain.ValidInt64{
Value: int32(leagueIDInt), Value: leagueIDInt,
Valid: true, Valid: true,
} }
} }
@ -61,11 +59,9 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if sportIDQuery != "" { if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery) sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil { if err != nil {
h.mongoLoggerSvc.Info("invalid sport id", domain.BadRequestLogger.Info("invalid sport id",
zap.String("sportID", sportIDQuery), zap.String("sportID", sportIDQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") return fiber.NewError(fiber.StatusBadRequest, "invalid sport id")
} }
@ -86,11 +82,9 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if firstStartTimeQuery != "" { if firstStartTimeQuery != "" {
firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery) firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
if err != nil { if err != nil {
h.mongoLoggerSvc.Info("invalid start_time format", domain.BadRequestLogger.Info("invalid start_time format",
zap.String("first_start_time", firstStartTimeQuery), zap.String("first_start_time", firstStartTimeQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
} }
@ -105,11 +99,9 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if lastStartTimeQuery != "" { if lastStartTimeQuery != "" {
lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery) lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery)
if err != nil { if err != nil {
h.mongoLoggerSvc.Info("invalid last_start_time format", domain.BadRequestLogger.Info("invalid last_start_time format",
zap.String("last_start_time", lastStartTimeQuery), zap.String("last_start_time", lastStartTimeQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
} }
@ -130,10 +122,9 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if isFeaturedQuery != "" { if isFeaturedQuery != "" {
isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery) isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to parse isFeatured", domain.BadRequestLogger.Error("Failed to parse isFeatured",
zap.Int("status_code", fiber.StatusBadRequest), zap.String("is_featured", isFeaturedQuery),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_shop_bet") return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_shop_bet")
} }
@ -159,15 +150,172 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
// fmt.Printf("League ID: %v", leagueID) // fmt.Printf("League ID: %v", leagueID)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to retrieve all upcoming events", domain.InternalServerErrorLogger.Error("Failed to retrieve all upcoming events",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
return response.WritePaginatedJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", events, nil, page, int(total)) res := domain.ConvertEventResList(events)
return response.WritePaginatedJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", res, nil, page, int(total))
}
// @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.UpcomingEvent
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/events [get]
func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.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 {
domain.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 {
domain.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 {
domain.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 {
domain.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 {
domain.BadRequestLogger.Error("Failed to parse isFeatured",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_shop_bet")
}
isFeatured = domain.ValidBool{
Value: isFeaturedParsed,
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,
})
// fmt.Printf("League ID: %v", leagueID)
if err != nil {
domain.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))
} }
@ -180,7 +328,7 @@ type TopLeague struct {
LeagueName string `json:"league_name"` LeagueName string `json:"league_name"`
LeagueCC string `json:"league_cc"` LeagueCC string `json:"league_cc"`
LeagueSportID int32 `json:"league_sport_id"` LeagueSportID int32 `json:"league_sport_id"`
Events []domain.UpcomingEvent `json:"events"` Events []domain.EventWithSettingsRes `json:"events"`
// Total int64 `json:"total"` // Total int64 `json:"total"`
} }
@ -191,43 +339,51 @@ type TopLeague struct {
// @Produce json // @Produce json
// @Success 200 {array} TopLeague // @Success 200 {array} TopLeague
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/top-leagues [get] // @Router /api/v1/{tenant_slug}/top-leagues [get]
func (h *Handler) GetTopLeagues(c *fiber.Ctx) error { func (h *Handler) GetTopLeagues(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
leagues, err := h.leagueSvc.GetFeaturedLeagues(c.Context()) leagues, err := h.leagueSvc.GetAllLeaguesByCompany(c.Context(), companyID.Value, domain.LeagueFilter{
IsFeatured: domain.ValidBool{
Value: true,
Valid: true,
},
})
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Error while fetching top leagues", domain.InternalServerErrorLogger.Error("Error while fetching top leagues",
zap.Int("status_code", fiber.StatusInternalServerError), zap.Int64("company_id", companyID.Value),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
var topLeague []TopLeague = make([]TopLeague, 0, len(leagues)) var topLeague []TopLeague = make([]TopLeague, 0, len(leagues))
for _, league := range leagues { for _, league := range leagues {
events, _, err := h.eventSvc.GetPaginatedUpcomingEvents( events, _, err := h.eventSvc.GetEventsWithSettings(
c.Context(), domain.EventFilter{ c.Context(), companyID.Value, domain.EventFilter{
LeagueID: domain.ValidInt32{ LeagueID: domain.ValidInt64{
Value: int32(league.ID), Value: league.ID,
Valid: true, Valid: true,
}, },
}) })
if err != nil { if err != nil {
h.mongoLoggerSvc.Warn("Error while fetching events for top league", domain.InternalServerErrorLogger.Warn("Error while fetching events for top league",
zap.Int64("LeagueID", league.ID), zap.Int64("LeagueID", league.ID),
zap.Int("status_code", fiber.StatusInternalServerError), zap.Int64("company_id", companyID.Value),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
} }
topLeague = append(topLeague, TopLeague{ topLeague = append(topLeague, TopLeague{
LeagueID: league.ID, LeagueID: league.ID,
LeagueName: league.Name, LeagueName: league.Name,
LeagueCC: league.CountryCode, LeagueCC: league.CountryCode.Value,
LeagueSportID: league.SportID, LeagueSportID: league.SportID,
Events: events, Events: domain.ConvertEventWithSettingResList(events),
}) })
} }
@ -252,26 +408,60 @@ func (h *Handler) GetUpcomingEventByID(c *fiber.Ctx) error {
id := c.Params("id") id := c.Params("id")
if id == "" { if id == "" {
h.mongoLoggerSvc.Info("Failed to parse event id", domain.BadRequestLogger.Info("Failed to parse event id", zap.String("id", id))
zap.String("id", id),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Missing id") return fiber.NewError(fiber.StatusBadRequest, "Missing id")
} }
event, err := h.eventSvc.GetUpcomingEventByID(c.Context(), id) event, err := h.eventSvc.GetUpcomingEventByID(c.Context(), id)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to get upcoming event by id", domain.InternalServerErrorLogger.Error("Failed to get upcoming event by id",
zap.String("eventID", id), zap.String("eventID", id),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
return response.WriteJSON(c, fiber.StatusOK, "Upcoming event retrieved successfully", event, nil) res := domain.ConvertEventRes(event)
return response.WriteJSON(c, fiber.StatusOK, "Upcoming event retrieved successfully", res, nil)
}
// @Summary Retrieve an upcoming by ID
// @Description Retrieve an upcoming event by ID
// @Tags prematch
// @Accept json
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} domain.UpcomingEvent
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}events/{id} [get]
func (h *Handler) GetTenantEventByID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
id := c.Params("id")
if id == "" {
domain.BadRequestLogger.Info("Failed to parse event id", zap.String("id", id))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
event, err := h.eventSvc.GetEventWithSettingByID(c.Context(), id, companyID.Value)
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to get upcoming event by id",
zap.String("eventID", id),
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
res := domain.ConvertEventWitSettingRes(event)
return response.WriteJSON(c, fiber.StatusOK, "Upcoming event retrieved successfully", res, nil)
} }
@ -294,11 +484,9 @@ func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error {
err := h.eventSvc.UpdateEventStatus(c.Context(), eventID, domain.STATUS_REMOVED) err := h.eventSvc.UpdateEventStatus(c.Context(), eventID, domain.STATUS_REMOVED)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to update event status", domain.InternalServerErrorLogger.Error("Failed to update event status",
zap.String("EventID", eventID), zap.String("EventID", eventID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update event status") return fiber.NewError(fiber.StatusInternalServerError, "Failed to update event status")
} }
@ -307,13 +495,15 @@ func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error {
} }
type UpdateEventFeaturedReq struct { type UpdateEventSettingsReq struct {
Featured bool `json:"is_featured" example:"true"` Featured *bool `json:"is_featured" example:"true"`
IsActive *bool `json:"is_active" example:"true"`
WinningUpperLimit *int `json:"winning_upper_limit" example:"10000"`
} }
// UpdateEventFeatured godoc // UpdateEventSettings godoc
// @Summary update the event featured // @Summary update the event settings
// @Description Update the event featured // @Description Update the event settings
// @Tags event // @Tags event
// @Accept json // @Accept json
// @Produce json // @Produce json
@ -321,44 +511,53 @@ type UpdateEventFeaturedReq struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/events/{id}/flag [put] // @Router /api/v1/{tenant_slug}/events/{id}/settings [put]
func (h *Handler) UpdateEventFeatured(c *fiber.Ctx) error { func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
eventID := c.Params("id") companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req UpdateEventFeaturedReq eventID := c.Params("id")
var req UpdateEventSettingsReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse user id", domain.BadRequestLogger.Info("Failed to parse user id",
zap.String("eventID", eventID), zap.String("eventID", eventID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, err.Error()) return fiber.NewError(fiber.StatusBadRequest, err.Error())
} }
logFields := []zap.Field{
zap.String("eventID", eventID),
zap.Int64("companyID", companyID.Value),
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) valErrs, ok := h.validator.Validate(c, req)
if !ok { if !ok {
var errMsg string var errMsg string
for field, msg := range valErrs { for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg) errMsg += fmt.Sprintf("%s: %s; ", field, msg)
} }
h.mongoLoggerSvc.Error("Failed to update event featured", domain.BadRequestLogger.Error("Failed to update event featured",
zap.Any("request", req), append(logFields, zap.String("errMsg", errMsg))...,
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
err := h.eventSvc.UpdateEventFeatured(c.Context(), eventID, req.Featured) err := h.eventSvc.UpdateEventSettings(c.Context(), domain.CreateEventSettings{
CompanyID: companyID.Value,
EventID: eventID,
IsFeatured: domain.ConvertBoolPtr(req.Featured),
IsActive: domain.ConvertBoolPtr(req.IsActive),
WinningUpperLimit: domain.ConvertIntPtr(req.WinningUpperLimit),
})
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to update event featured", domain.InternalServerErrorLogger.Error("Failed to update event featured", append(logFields, zap.Error(err))...)
zap.String("eventID", eventID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }

View File

@ -3,7 +3,6 @@ package handlers
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
@ -51,11 +50,9 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
if sportIDQuery != "" { if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery) sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil { if err != nil {
h.mongoLoggerSvc.Info("invalid sport id", domain.BadRequestLogger.Info("invalid sport id",
zap.String("sport_id", sportIDQuery), zap.String("sport_id", sportIDQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err), zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") return fiber.NewError(fiber.StatusBadRequest, "invalid sport id")
} }
@ -65,6 +62,23 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
} }
} }
queryLogFields := []zap.Field{
zap.Int("page", page),
zap.Int("page_size", pageSize),
zap.Int64("limit_value", limit.Value),
zap.Bool("limit_valid", limit.Valid),
zap.Int64("offset_value", offset.Value),
zap.Bool("offset_valid", offset.Valid),
zap.Bool("is_active_value", isActive.Value),
zap.Bool("is_active_valid", isActive.Valid),
zap.String("cc_query", countryCodeQuery),
zap.String("cc", countryCode.Value),
zap.Bool("cc_valid", countryCode.Valid),
zap.String("sport_id_query", sportIDQuery),
zap.Int32("sport_id", sportID.Value),
zap.Bool("sport_id_valid", sportID.Valid),
}
leagues, err := h.leagueSvc.GetAllLeagues(c.Context(), domain.LeagueFilter{ leagues, err := h.leagueSvc.GetAllLeagues(c.Context(), domain.LeagueFilter{
CountryCode: countryCode, CountryCode: countryCode,
IsActive: isActive, IsActive: isActive,
@ -75,16 +89,106 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
if err != nil { if err != nil {
fmt.Printf("Error fetching league %v \n", err) fmt.Printf("Error fetching league %v \n", err)
h.mongoLoggerSvc.Error("Failed to get all leagues", domain.InternalServerErrorLogger.Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))...,
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error()) return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error())
} }
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", leagues, nil) return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", leagues, nil)
} }
// GetAllLeaguesForTenant godoc
// @Summary Gets all leagues
// @Description Gets all leagues
// @Tags leagues
// @Accept json
// @Produce json
// @Success 200 {array} domain.League
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/leagues [get]
func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.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.ValidInt64{
Value: int64(pageSize),
Valid: pageSize == 0,
}
offset := domain.ValidInt64{
Value: int64(page - 1),
Valid: true,
}
countryCodeQuery := c.Query("cc")
countryCode := domain.ValidString{
Value: countryCodeQuery,
Valid: countryCodeQuery != "",
}
isActiveQuery := c.QueryBool("is_active", false)
isActiveFilter := c.QueryBool("is_active_filter", false)
isActive := domain.ValidBool{
Value: isActiveQuery,
Valid: isActiveFilter,
}
sportIDQuery := c.Query("sport_id")
var sportID domain.ValidInt32
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid sport id",
zap.String("sport_id", sportIDQuery),
zap.Error(err),
)
return fiber.NewError(fiber.StatusBadRequest, "invalid sport id")
}
sportID = domain.ValidInt32{
Value: int32(sportIDint),
Valid: true,
}
}
queryLogFields := []zap.Field{
zap.Int64("company_id", companyID.Value),
zap.Bool("company_valid", companyID.Valid),
zap.Int("page", page),
zap.Int("page_size", pageSize),
zap.Int64("limit_value", limit.Value),
zap.Bool("limit_valid", limit.Valid),
zap.Int64("offset_value", offset.Value),
zap.Bool("offset_valid", offset.Valid),
zap.Bool("is_active_value", isActive.Value),
zap.Bool("is_active_valid", isActive.Valid),
zap.String("cc_query", countryCodeQuery),
zap.String("cc", countryCode.Value),
zap.Bool("cc_valid", countryCode.Valid),
zap.String("sport_id_query", sportIDQuery),
zap.Int32("sport_id", sportID.Value),
zap.Bool("sport_id_valid", sportID.Valid),
}
leagues, err := h.leagueSvc.GetAllLeaguesByCompany(c.Context(), companyID.Value, domain.LeagueFilter{
CountryCode: countryCode,
IsActive: isActive,
SportID: sportID,
Limit: limit,
Offset: offset,
})
if err != nil {
fmt.Printf("Error fetching league %v \n", err)
domain.InternalServerErrorLogger.Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", leagues, nil)
}
type SetLeagueActiveReq struct { type SetLeagueActiveReq struct {
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
} }
@ -100,8 +204,13 @@ type SetLeagueActiveReq struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/leagues/{id}/set-active [put] // @Router /api/v1/{tenant_slug}/leagues/{id}/set-active [put]
func (h *Handler) SetLeagueActive(c *fiber.Ctx) error { func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.mongoLoggerSvc.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
leagueIdStr := c.Params("id") leagueIdStr := c.Params("id")
if leagueIdStr == "" { if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id") return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
@ -111,51 +220,44 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "invalid league id") return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
} }
queryLogFields := []zap.Field{
zap.Int64("company_id", companyID.Value),
zap.Bool("company_valid", companyID.Valid),
zap.String("league_id", leagueIdStr),
}
var req SetLeagueActiveReq var req SetLeagueActiveReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.logger.Error("SetLeagueReq failed", "error", err) domain.InternalServerErrorLogger.Error("SetLeagueReq failed to parse request body",
h.mongoLoggerSvc.Error("SetLeagueReq failed to parse request body", append(queryLogFields, zap.Error(err))...,
zap.Any("request", req),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
) )
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request:"+err.Error()) return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request:"+err.Error())
} }
queryLogFields = append(queryLogFields, zap.Any("is_active", req.IsActive))
valErrs, ok := h.validator.Validate(c, req) valErrs, ok := h.validator.Validate(c, req)
if !ok { if !ok {
var errMsg string var errMsg string
for field, msg := range valErrs { for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg) errMsg += fmt.Sprintf("%s: %s; ", field, msg)
} }
h.mongoLoggerSvc.Info("Failed to validate SetLeagueActiveReq", domain.BadRequestLogger.Info("Failed to validate SetLeagueActiveReq", append(queryLogFields, zap.Error(err))...)
zap.Any("request", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
if err := h.leagueSvc.SetLeagueActive(c.Context(), int64(leagueId), req.IsActive); err != nil { if err := h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{
h.mongoLoggerSvc.Error("Failed to update league active", LeagueID: int64(leagueId),
zap.Int64("leagueID", int64(leagueId)), CompanyID: companyID.Value,
zap.Bool("is_active", req.IsActive), IsActive: domain.ValidBool{
zap.Int("status_code", fiber.StatusInternalServerError), Value: req.IsActive,
zap.Error(err), Valid: true,
zap.Time("timestamp", time.Now()), },
) }); err != nil {
domain.InternalServerErrorLogger.Error("Failed to update league active", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error()) return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error())
} }
h.mongoLoggerSvc.Info("League Active has been successfully updated", domain.SuccessResLogger.Info("League Active has been successfully updated", queryLogFields...)
zap.Int64("userID", int64(leagueId)),
zap.Int64("leagueID", int64(leagueId)),
zap.Bool("is_active", req.IsActive),
zap.Int("status_code", fiber.StatusOK),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil) return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
} }
@ -175,8 +277,13 @@ type SetLeagueAsFeatured struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/leagues/{id}/featured [put] // @Router /api/v1/{tenant_slug}/leagues/{id}/featured [put]
func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error { func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.mongoLoggerSvc.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
leagueIdStr := c.Params("id") leagueIdStr := c.Params("id")
if leagueIdStr == "" { if leagueIdStr == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing league id") return fiber.NewError(fiber.StatusBadRequest, "Missing league id")
@ -186,54 +293,41 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "invalid league id") return fiber.NewError(fiber.StatusBadRequest, "invalid league id")
} }
queryLogFields := []zap.Field{
zap.Int64("company_id", companyID.Value),
zap.Bool("company_valid", companyID.Valid),
zap.String("league_id", leagueIdStr),
}
var req SetLeagueAsFeatured var req SetLeagueAsFeatured
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.logger.Error("SetLeagueFeaturedReq failed", "error", err) domain.BadRequestLogger.Info("SetLeagueFeaturedReq failed to parse request body", append(queryLogFields, zap.Error(err))...)
h.mongoLoggerSvc.Info("SetLeagueFeaturedReq failed to parse request body",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request body:"+err.Error()) return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request body:"+err.Error())
} }
valErrs, ok := h.validator.Validate(c, req) valErrs, ok := h.validator.Validate(c, req)
queryLogFields = append(queryLogFields, zap.Bool("is_featured", req.IsFeatured))
if !ok { if !ok {
var errMsg string var errMsg string
for field, msg := range valErrs { for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg) errMsg += fmt.Sprintf("%s: %s; ", field, msg)
} }
h.mongoLoggerSvc.Info("Failed to validate SetLeagueFeaturedReq", domain.BadRequestLogger.Info("Failed to validate SetLeagueFeaturedReq", append(queryLogFields, zap.Error(err))...)
zap.Any("request", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
err = h.leagueSvc.UpdateLeague(c.Context(), domain.UpdateLeague{ err = h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{
ID: int64(leagueId), LeagueID: int64(leagueId),
CompanyID: companyID.Value,
IsFeatured: domain.ValidBool{ IsFeatured: domain.ValidBool{
Value: req.IsFeatured, Value: req.IsFeatured,
Valid: true, Valid: true,
}, },
}) })
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to update league", domain.InternalServerErrorLogger.Error("Failed to update league", append(queryLogFields, zap.Error(err))...)
zap.Int64("leagueID", int64(leagueId)),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error()) return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error())
} }
h.mongoLoggerSvc.Info("League Featured has been successfully updated",
zap.Int64("userID", int64(leagueId)), domain.SuccessResLogger.Info("League Featured has been successfully updated", queryLogFields...)
zap.Int64("leagueID", int64(leagueId)),
zap.Bool("is_featured", req.IsFeatured),
zap.Int("status_code", fiber.StatusOK),
zap.Time("timestamp", time.Now()),
)
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil) return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
} }

View File

@ -1,35 +1,109 @@
package handlers package handlers
import ( import (
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go.uber.org/zap" "go.uber.org/zap"
"strconv"
) )
// GetALLPrematchOdds // GetAllOdds
// @Summary Retrieve all prematch odds // @Summary Retrieve all odds
// @Description Retrieve all prematch odds from the database // @Description Retrieve all odds from the database
// @Tags prematch // @Tags prematch
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Success 200 {array} domain.Odd // @Success 200 {array} domain.Odd
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/odds [get] // @Router /api/v1/odds [get]
func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error { func (h *Handler) GetAllOdds(c *fiber.Ctx) error {
odds, err := h.prematchSvc.GetALLPrematchOdds(c.Context()) limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
domain.BadRequestLogger.Info("Invalid limit value", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value")
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
domain.BadRequestLogger.Info("Invalid offset value", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
odds, err := h.prematchSvc.GetAllOdds(c.Context(), domain.OddMarketFilter{
Limit: domain.ValidInt32{
Value: int32(limit),
Valid: true,
},
Offset: domain.ValidInt32{
Value: int32(offset),
Valid: true,
},
})
if err != nil { if err != nil {
logFields := append([]zap.Field{}, domain.InternalServerErrorZapFields...) domain.InternalServerErrorLogger.Error("Failed to retrieve all odds",
logFields = append(logFields, zap.Error(err)) zap.Int("limit", limit),
h.mongoLoggerSvc.Error("Failed to retrieve all prematch odds", logFields...) zap.Int("offset", offset),
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
return response.WriteJSON(c, fiber.StatusOK, "All prematch odds retrieved successfully", odds, nil) return response.WriteJSON(c, fiber.StatusOK, "All odds retrieved successfully", odds, nil)
} }
// GetRawOddsByMarketID // GetAllOdds
// @Summary Retrieve all odds
// @Description Retrieve all odds from the database
// @Tags prematch
// @Accept json
// @Produce json
// @Success 200 {array} domain.Odd
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/odds [get]
func (h *Handler) GetAllTenantOdds(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
domain.BadRequestLogger.Info("Invalid limit value", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value")
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
domain.BadRequestLogger.Info("Invalid offset value", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
odds, err := h.prematchSvc.GetAllOddsWithSettings(c.Context(), companyID.Value, domain.OddMarketFilter{
Limit: domain.ValidInt32{
Value: int32(limit),
Valid: true,
},
Offset: domain.ValidInt32{
Value: int32(offset),
Valid: true,
},
})
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to retrieve all odds",
zap.Int("limit", limit),
zap.Int("offset", offset),
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All odds retrieved successfully", odds, nil)
}
// GetOddsByMarketID
// @Summary Retrieve raw odds by Market ID // @Summary Retrieve raw odds by Market ID
// @Description Retrieve raw odds records using a Market ID // @Description Retrieve raw odds records using a Market ID
// @Tags prematch // @Tags prematch
@ -41,7 +115,7 @@ func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error {
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/odds/upcoming/{upcoming_id}/market/{market_id} [get] // @Router /api/v1/odds/upcoming/{upcoming_id}/market/{market_id} [get]
func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error { func (h *Handler) GetOddsByMarketID(c *fiber.Ctx) error {
logFields := []zap.Field{ logFields := []zap.Field{
zap.String("market_id", c.Params("market_id")), zap.String("market_id", c.Params("market_id")),
zap.String("upcoming_id", c.Params("upcoming_id")), zap.String("upcoming_id", c.Params("upcoming_id")),
@ -49,21 +123,20 @@ func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error {
marketID := c.Params("market_id") marketID := c.Params("market_id")
if marketID == "" { if marketID == "" {
h.mongoLoggerSvc.Info("Missing market_id", append(logFields, domain.BadRequestZapFields...)...) domain.BadRequestLogger.Info("Missing market_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing market_id") return fiber.NewError(fiber.StatusBadRequest, "Missing market_id")
} }
upcomingID := c.Params("upcoming_id") upcomingID := c.Params("upcoming_id")
if upcomingID == "" { if upcomingID == "" {
h.mongoLoggerSvc.Info("Missing upcoming_id", append(logFields, domain.BadRequestZapFields...)...) domain.BadRequestLogger.Info("Missing upcoming_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id") return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id")
} }
rawOdds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketID, upcomingID) rawOdds, err := h.prematchSvc.GetOddsByMarketID(c.Context(), marketID, upcomingID)
if err != nil { if err != nil {
// Lets turn this into a warn because this is constantly going off // Lets turn this into a warn because this is constantly going off
logFields = append(logFields, zap.Error(err)) domain.InternalServerErrorLogger.Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...)
h.mongoLoggerSvc.Warn("Failed to get raw odds by market ID", append(logFields, domain.InternalServerErrorZapFields...)...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
@ -71,6 +144,55 @@ func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error {
} }
// GetTenantOddsByMarketID
// @Summary Retrieve raw odds by Market ID
// @Description Retrieve raw odds records using a Market ID
// @Tags prematch
// @Accept json
// @Produce json
// @Param upcoming_id path string true "Upcoming ID"
// @Param market_id path string true "Market ID"
// @Success 200 {array} domain.RawOddsByMarketID
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/odds/upcoming/{upcoming_id}/market/{market_id} [get]
func (h *Handler) GetTenantOddsByMarketID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
logFields := []zap.Field{
zap.String("market_id", c.Params("market_id")),
zap.String("upcoming_id", c.Params("upcoming_id")),
zap.Int64("company_id", companyID.Value),
}
marketID := c.Params("market_id")
if marketID == "" {
domain.BadRequestLogger.Info("Missing market_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing market_id")
}
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
domain.BadRequestLogger.Info("Missing upcoming_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id")
}
oddMarket, err := h.prematchSvc.GetOddsWithSettingsByMarketID(c.Context(), marketID, upcomingID, companyID.Value)
if err != nil {
// Lets turn this into a warn because this is constantly going off
domain.InternalServerErrorLogger.Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", oddMarket, nil)
}
// GetOddsByUpcomingID
// @Summary Retrieve prematch odds by upcoming ID (FI) // @Summary Retrieve prematch odds by upcoming ID (FI)
// @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination // @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination
// @Tags prematch // @Tags prematch
@ -92,31 +214,89 @@ func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error {
upcomingID := c.Params("upcoming_id") upcomingID := c.Params("upcoming_id")
if upcomingID == "" { if upcomingID == "" {
h.mongoLoggerSvc.Info("Missing upcoming_id", append(logFields, domain.BadRequestZapFields...)...) domain.BadRequestLogger.Info("Missing upcoming_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id") return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id")
} }
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10 limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 { if err != nil || limit <= 0 {
logFields = append(logFields, zap.Error(err)) logFields = append(logFields, zap.Error(err))
h.mongoLoggerSvc.Info("Invalid limit value", append(logFields, domain.BadRequestZapFields...)...) domain.BadRequestLogger.Info("Invalid limit value", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value") return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value")
} }
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0 offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 { if err != nil || offset < 0 {
logFields = append(logFields, zap.Error(err)) logFields = append(logFields, zap.Error(err))
h.mongoLoggerSvc.Info("Invalid offset value", append(logFields, domain.BadRequestZapFields...)...) domain.BadRequestLogger.Info("Invalid offset value", logFields...)
return fiber.NewError(fiber.StatusBadRequest, err.Error()) return fiber.NewError(fiber.StatusBadRequest, err.Error())
} }
odds, err := h.prematchSvc.GetPrematchOddsByUpcomingID(c.Context(), upcomingID) odds, err := h.prematchSvc.GetOddsByEventID(c.Context(), upcomingID, domain.OddMarketWithEventFilter{})
if err != nil { if err != nil {
logFields = append(logFields, zap.Error(err)) logFields = append(logFields, zap.Error(err))
h.mongoLoggerSvc.Error("Failed to retrieve prematch odds", append(logFields, domain.InternalServerErrorZapFields...)...) domain.InternalServerErrorLogger.Error("Failed to retrieve odds", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve prematch odds"+err.Error()) return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve odds"+err.Error())
} }
return response.WriteJSON(c, fiber.StatusOK, "Prematch odds retrieved successfully", odds, nil) return response.WriteJSON(c, fiber.StatusOK, "Odds retrieved successfully", odds, nil)
}
// GetTenantOddsByUpcomingID
// @Summary Retrieve prematch odds by upcoming ID (FI)
// @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination
// @Tags prematch
// @Accept json
// @Produce json
// @Param upcoming_id path string true "Upcoming Event ID (FI)"
// @Param limit query int false "Number of results to return (default: 10)"
// @Param offset query int false "Number of results to skip (default: 0)"
// @Success 200 {array} domain.Odd
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/odds/upcoming/{upcoming_id} [get]
func (h *Handler) GetTenantOddsByUpcomingID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
logFields := []zap.Field{
zap.String("upcoming_id", c.Params("upcoming_id")),
zap.String("limit_param", c.Query("limit", "10")),
zap.String("offset_param", c.Query("offset", "0")),
zap.Int64("company_id", companyID.Value),
}
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
domain.BadRequestLogger.Info("Missing upcoming_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id")
}
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
logFields = append(logFields, zap.Error(err))
domain.BadRequestLogger.Info("Invalid limit value", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value")
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
logFields = append(logFields, zap.Error(err))
domain.BadRequestLogger.Info("Invalid offset value", logFields...)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
odds, err := h.prematchSvc.GetOddsWithSettingsByEventID(c.Context(), upcomingID, companyID.Value, domain.OddMarketFilter{})
if err != nil {
logFields = append(logFields, zap.Error(err))
domain.InternalServerErrorLogger.Error("Failed to retrieve odds", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve odds"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Odds retrieved successfully", odds, nil)
} }

View File

@ -33,9 +33,13 @@ type CheckPhoneEmailExistRes struct {
// @Success 200 {object} CheckPhoneEmailExistRes // @Success 200 {object} CheckPhoneEmailExistRes
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/checkPhoneEmailExist [post] // @Router /api/v1/{tenant_slug}/user/checkPhoneEmailExist [post]
func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error { func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req CheckPhoneEmailExistReq var req CheckPhoneEmailExistReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse CheckPhoneEmailExist request", h.mongoLoggerSvc.Info("Failed to parse CheckPhoneEmailExist request",
@ -54,7 +58,7 @@ func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg) return fiber.NewError(fiber.StatusBadRequest, errMsg)
} }
emailExist, phoneExist, err := h.userSvc.CheckPhoneEmailExist(c.Context(), req.PhoneNumber, req.Email) emailExist, phoneExist, err := h.userSvc.CheckPhoneEmailExist(c.Context(), req.PhoneNumber, req.Email, companyID)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to check phone/email existence", h.mongoLoggerSvc.Error("Failed to check phone/email existence",
zap.Any("request", req), zap.Any("request", req),
@ -87,9 +91,13 @@ type RegisterCodeReq struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/sendRegisterCode [post] // @Router /api/v1/{tenant_slug}/user/sendRegisterCode [post]
func (h *Handler) SendRegisterCode(c *fiber.Ctx) error { func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req RegisterCodeReq var req RegisterCodeReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse SendRegisterCode request", h.mongoLoggerSvc.Info("Failed to parse SendRegisterCode request",
@ -120,7 +128,7 @@ func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided") return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
} }
if err := h.userSvc.SendRegisterCode(c.Context(), medium, sentTo, domain.AfroMessage); err != nil { if err := h.userSvc.SendRegisterCode(c.Context(), medium, sentTo, domain.AfroMessage, companyID); err != nil {
h.mongoLoggerSvc.Error("Failed to send register code", h.mongoLoggerSvc.Error("Failed to send register code",
zap.String("Medium", string(medium)), zap.String("Medium", string(medium)),
zap.String("Send To", string(sentTo)), zap.String("Send To", string(sentTo)),
@ -154,9 +162,13 @@ type RegisterUserReq struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/register [post] // @Router /api/v1/{tenant_slug}/user/register [post]
func (h *Handler) RegisterUser(c *fiber.Ctx) error { func (h *Handler) RegisterUser(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req RegisterUserReq var req RegisterUserReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse RegisterUser request", h.mongoLoggerSvc.Info("Failed to parse RegisterUser request",
@ -183,6 +195,8 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
Otp: req.Otp, Otp: req.Otp,
ReferralCode: req.ReferalCode, ReferralCode: req.ReferalCode,
OtpMedium: domain.OtpMediumEmail, OtpMedium: domain.OtpMediumEmail,
CompanyID: companyID,
Role: string(domain.RoleCustomer),
} }
medium, err := getMedium(req.Email, req.PhoneNumber) medium, err := getMedium(req.Email, req.PhoneNumber)
if err != nil { if err != nil {
@ -279,9 +293,13 @@ type ResetCodeReq struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/sendResetCode [post] // @Router /api/v1/{tenant_slug}/user/sendResetCode [post]
func (h *Handler) SendResetCode(c *fiber.Ctx) error { func (h *Handler) SendResetCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req ResetCodeReq var req ResetCodeReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse SendResetCode request", h.mongoLoggerSvc.Info("Failed to parse SendResetCode request",
@ -318,7 +336,7 @@ func (h *Handler) SendResetCode(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided") return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
} }
if err := h.userSvc.SendResetCode(c.Context(), medium, sentTo, domain.AfroMessage); err != nil { if err := h.userSvc.SendResetCode(c.Context(), medium, sentTo, domain.AfroMessage, companyID); err != nil {
h.mongoLoggerSvc.Error("Failed to send reset code", h.mongoLoggerSvc.Error("Failed to send reset code",
zap.String("medium", string(medium)), zap.String("medium", string(medium)),
zap.String("sentTo", string(sentTo)), zap.String("sentTo", string(sentTo)),
@ -349,7 +367,7 @@ type ResetPasswordReq struct {
// @Success 200 {object} response.APIResponse // @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/resetPassword [post] // @Router /api/v1/{tenant_slug}/user/resetPassword [post]
func (h *Handler) ResetPassword(c *fiber.Ctx) error { func (h *Handler) ResetPassword(c *fiber.Ctx) error {
var req ResetPasswordReq var req ResetPasswordReq
@ -447,7 +465,7 @@ type CustomerProfileRes struct {
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Security Bearer // @Security Bearer
// @Router /api/v1/user/customer-profile [get] // @Router /api/v1/{tenant_slug}/user/customer-profile [get]
func (h *Handler) CustomerProfile(c *fiber.Ctx) error { func (h *Handler) CustomerProfile(c *fiber.Ctx) error {
userID, ok := c.Locals("user_id").(int64) userID, ok := c.Locals("user_id").(int64)
@ -499,7 +517,6 @@ func (h *Handler) CustomerProfile(c *fiber.Ctx) error {
SuspendedAt: user.SuspendedAt, SuspendedAt: user.SuspendedAt,
Suspended: user.Suspended, Suspended: user.Suspended,
LastLogin: *lastLogin, LastLogin: *lastLogin,
} }
return response.WriteJSON(c, fiber.StatusOK, "User profile retrieved successfully", res, nil) return response.WriteJSON(c, fiber.StatusOK, "User profile retrieved successfully", res, nil)
} }
@ -530,7 +547,7 @@ type AdminProfileRes struct {
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Security Bearer // @Security Bearer
// @Router /api/v1/user/admin-profile [get] // @Router /api/v1/{tenant_slug}/user/admin-profile [get]
func (h *Handler) AdminProfile(c *fiber.Ctx) error { func (h *Handler) AdminProfile(c *fiber.Ctx) error {
userID, ok := c.Locals("user_id").(int64) userID, ok := c.Locals("user_id").(int64)
@ -612,7 +629,7 @@ type SearchUserByNameOrPhoneReq struct {
// @Success 200 {object} UserProfileRes // @Success 200 {object} UserProfileRes
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/search [post] // @Router /api/v1/{tenant_slug}/user/search [post]
func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error { func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
// TODO: Add filtering by role based on which user is calling this // TODO: Add filtering by role based on which user is calling this
var req SearchUserByNameOrPhoneReq var req SearchUserByNameOrPhoneReq
@ -870,7 +887,7 @@ func (h *Handler) UpdateUserSuspend(c *fiber.Ctx) error {
// @Success 200 {array} domain.BetRes // @Success 200 {array} domain.BetRes
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/bets [get] // @Router /api/v1/{tenant_slug}/user/bets [get]
func (h *Handler) GetBetByUserID(c *fiber.Ctx) error { func (h *Handler) GetBetByUserID(c *fiber.Ctx) error {
userID, ok := c.Locals("user_id").(int64) userID, ok := c.Locals("user_id").(int64)
if !ok || userID == 0 { if !ok || userID == 0 {

View File

@ -313,7 +313,7 @@ func (h *Handler) UpdateWalletActive(c *fiber.Ctx) error {
// @Success 200 {object} CustomerWalletRes // @Success 200 {object} CustomerWalletRes
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /api/v1/user/wallet [get] // @Router /api/v1/{tenant_slug}/user/wallet [get]
func (h *Handler) GetCustomerWallet(c *fiber.Ctx) error { func (h *Handler) GetCustomerWallet(c *fiber.Ctx) error {
userID, ok := c.Locals("user_id").(int64) userID, ok := c.Locals("user_id").(int64)

View File

@ -67,8 +67,8 @@ func (a *App) authMiddleware(c *fiber.Ctx) error {
// return fiber.NewError(fiber.StatusUnauthorized, "Refresh token missing") // return fiber.NewError(fiber.StatusUnauthorized, "Refresh token missing")
} }
// Asserting to make sure that there is no company role without a valid company id // Asserting to make sure that only the super admin can have a nil company ID
if claim.Role != domain.RoleSuperAdmin && claim.Role != domain.RoleCustomer && !claim.CompanyID.Valid { if claim.Role != domain.RoleSuperAdmin && !claim.CompanyID.Valid {
a.mongoLoggerSvc.Error("Company Role without Company ID", a.mongoLoggerSvc.Error("Company Role without Company ID",
zap.Int64("userID", claim.UserId), zap.Int64("userID", claim.UserId),
zap.Int("status_code", fiber.StatusInternalServerError), zap.Int("status_code", fiber.StatusInternalServerError),
@ -220,3 +220,25 @@ func (a *App) WebsocketAuthMiddleware(c *fiber.Ctx) error {
) )
return c.Next() return c.Next()
} }
func (a *App) TenantMiddleware(c *fiber.Ctx) error {
if tokenCID, ok := c.Locals("company_id").(domain.ValidInt64); ok && tokenCID.Valid {
return c.Next()
}
tenantSlug := c.Params("tenant_slug")
if tenantSlug == "" {
return fiber.NewError(fiber.StatusBadRequest, "tenant is required for this route")
}
companyID, err := a.companySvc.GetCompanyIDBySlug(c.Context(), tenantSlug)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "failed to resolve tenant")
}
c.Locals("company_id", domain.ValidInt64{
Value: companyID,
Valid: true,
})
return c.Next()
}

View File

@ -62,7 +62,10 @@ func (a *App) initAppRoutes() {
}) })
}) })
// Groups
groupV1 := a.fiber.Group("/api/v1") groupV1 := a.fiber.Group("/api/v1")
tenant := groupV1.Group("/:tenant_slug", a.TenantMiddleware)
tenantAuth := groupV1.Group("/:tenant_slug", a.authMiddleware, a.TenantMiddleware)
//Direct_deposit //Direct_deposit
groupV1.Post("/direct_deposit", a.authMiddleware, h.InitiateDirectDeposit) groupV1.Post("/direct_deposit", a.authMiddleware, h.InitiateDirectDeposit)
@ -82,8 +85,9 @@ func (a *App) initAppRoutes() {
}) })
// Auth Routes // Auth Routes
groupV1.Post("/auth/customer-login", h.LoginCustomer) tenant.Post("/auth/customer-login", h.LoginCustomer)
groupV1.Post("/auth/admin-login", h.LoginAdmin) tenant.Post("/auth/admin-login", h.LoginAdmin)
groupV1.Post("/auth/super-login", h.LoginSuper)
groupV1.Post("/auth/refresh", h.RefreshToken) groupV1.Post("/auth/refresh", h.RefreshToken)
groupV1.Post("/auth/logout", a.authMiddleware, h.LogOutCustomer) groupV1.Post("/auth/logout", a.authMiddleware, h.LogOutCustomer)
groupV1.Get("/auth/test", a.authMiddleware, func(c *fiber.Ctx) error { groupV1.Get("/auth/test", a.authMiddleware, func(c *fiber.Ctx) error {
@ -139,20 +143,21 @@ func (a *App) initAppRoutes() {
// groupV1.Get("/arifpay/session-id/verify-transaction/:session_id", a.authMiddleware, h.ArifpayVerifyBySessionIDHandler) // groupV1.Get("/arifpay/session-id/verify-transaction/:session_id", a.authMiddleware, h.ArifpayVerifyBySessionIDHandler)
// User Routes // User Routes
groupV1.Post("/user/resetPassword", h.ResetPassword) tenant.Post("/user/resetPassword", h.ResetPassword)
groupV1.Post("/user/sendResetCode", h.SendResetCode) tenant.Post("/user/sendResetCode", h.SendResetCode)
groupV1.Post("/user/register", h.RegisterUser) tenant.Post("/user/register", h.RegisterUser)
groupV1.Post("/user/sendRegisterCode", h.SendRegisterCode) tenant.Post("/user/sendRegisterCode", h.SendRegisterCode)
groupV1.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist) tenant.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist)
groupV1.Get("/user/customer-profile", a.authMiddleware, h.CustomerProfile) tenantAuth.Get("/user/customer-profile", h.CustomerProfile)
groupV1.Get("/user/admin-profile", a.authMiddleware, h.AdminProfile) tenantAuth.Get("/user/admin-profile", h.AdminProfile)
groupV1.Get("/user/single/:id", a.authMiddleware, h.GetUserByID) tenantAuth.Get("/user/bets", h.GetBetByUserID)
groupV1.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser)
groupV1.Post("/user/suspend", a.authMiddleware, h.UpdateUserSuspend)
groupV1.Get("/user/bets", a.authMiddleware, h.GetBetByUserID)
groupV1.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet) groupV1.Get("/user/single/:id", a.authMiddleware, h.GetUserByID)
groupV1.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone) groupV1.Post("/user/suspend", a.authMiddleware, h.UpdateUserSuspend)
groupV1.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser)
tenantAuth.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet)
tenantAuth.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone)
// Referral Routes // Referral Routes
groupV1.Post("/referral/create", a.authMiddleware, h.CreateReferralCode) groupV1.Post("/referral/create", a.authMiddleware, h.CreateReferralCode)
@ -186,20 +191,28 @@ func (a *App) initAppRoutes() {
groupV1.Put("/managers/:id", a.authMiddleware, h.UpdateManagers) groupV1.Put("/managers/:id", a.authMiddleware, h.UpdateManagers)
groupV1.Get("/manager/:id/branch", a.authMiddleware, h.GetBranchByManagerID) groupV1.Get("/manager/:id/branch", a.authMiddleware, h.GetBranchByManagerID)
groupV1.Get("/odds", h.GetALLPrematchOdds) groupV1.Get("/odds", a.authMiddleware, a.SuperAdminOnly, h.GetAllOdds)
groupV1.Get("/odds/upcoming/:upcoming_id", h.GetOddsByUpcomingID) groupV1.Get("/odds/upcoming/:upcoming_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByUpcomingID)
groupV1.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetRawOddsByMarketID) groupV1.Get("/odds/upcoming/:upcoming_id/market/:market_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByMarketID)
groupV1.Get("/events", h.GetAllUpcomingEvents) tenant.Get("/odds", h.GetAllTenantOdds)
groupV1.Get("/events/:id", h.GetUpcomingEventByID) tenant.Get("/odds/upcoming/:upcoming_id", h.GetTenantOddsByUpcomingID)
tenant.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetTenantOddsByMarketID)
groupV1.Get("/events", a.authMiddleware, a.SuperAdminOnly, h.GetAllUpcomingEvents)
groupV1.Get("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.GetUpcomingEventByID)
groupV1.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved) groupV1.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved)
groupV1.Get("/top-leagues", h.GetTopLeagues)
groupV1.Put("/events/:id/featured", h.UpdateEventFeatured) tenant.Get("/events", h.GetTenantUpcomingEvents)
tenant.Get("/events/:id", h.GetTenantEventByID)
tenant.Get("/top-leagues", h.GetTopLeagues)
tenant.Put("/events/:id/settings", h.UpdateEventSettings)
// Leagues // Leagues
groupV1.Get("/leagues", h.GetAllLeagues) tenant.Get("/leagues", h.GetAllLeagues)
groupV1.Put("/leagues/:id/set-active", h.SetLeagueActive) tenant.Put("/leagues/:id/set-active", h.SetLeagueActive)
groupV1.Put("/leagues/:id/featured", h.SetLeagueFeatured) tenant.Put("/leagues/:id/featured", h.SetLeagueFeatured)
groupV1.Get("/leagues", a.authMiddleware, a.SuperAdminOnly, h.GetAllLeagues)
groupV1.Get("/result/:id", h.GetResultsByEventID) groupV1.Get("/result/:id", h.GetResultsByEventID)