diff --git a/db/data/seed_data.sql b/db/data/seed_data.sql index 256b882..732a346 100644 --- a/db/data/seed_data.sql +++ b/db/data/seed_data.sql @@ -1,59 +1,207 @@ BEGIN; - CREATE EXTENSION IF NOT EXISTS pgcrypto; - -- Users INSERT INTO users ( - id, first_name, last_name, email, phone_number, password, role, - email_verified, phone_verified, created_at, updated_at, suspended, company_id -) VALUES -(1, 'John', 'Doe', 'john.doe@example.com', NULL, - crypt('password123', gen_salt('bf'))::bytea, 'customer', - TRUE, FALSE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, FALSE, NULL), - -(2, 'Test', 'Admin', 'test.admin@gmail.com', '0988554466', - crypt('password123', gen_salt('bf'))::bytea, 'admin', - TRUE, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, FALSE, 1), - -(3, 'Samuel', 'Tariku', 'cybersamt@gmail.com', '0911111111', - crypt('password@123', gen_salt('bf'))::bytea, 'super_admin', - TRUE, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, FALSE, NULL), - -(4, 'Kirubel', 'Kibru', 'kirubel.jkl679@gmail.com', '0911554486', - crypt('password@123', gen_salt('bf'))::bytea, 'super_admin', - TRUE, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, FALSE, NULL); - + id, + first_name, + last_name, + email, + phone_number, + password, + role, + email_verified, + phone_verified, + created_at, + updated_at, + suspended, + company_id + ) +VALUES ( + 1, + 'John', + 'Doe', + 'john.doe@example.com', + NULL, + crypt('password@123', gen_salt('bf'))::bytea, + 'customer', + TRUE, + FALSE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + FALSE, + NULL + ), + ( + 2, + 'Test', + 'Admin', + 'test.admin@gmail.com', + '0988554466', + crypt('password123', gen_salt('bf'))::bytea, + 'admin', + TRUE, + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + FALSE, + 1 + ), + ( + 3, + 'Samuel', + 'Tariku', + 'cybersamt@gmail.com', + '0911111111', + crypt('password@123', gen_salt('bf'))::bytea, + 'super_admin', + TRUE, + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + FALSE, + NULL + ), + ( + 4, + 'Kirubel', + 'Kibru', + 'kirubel.jkl679@gmail.com', + '0911554486', + crypt('password@123', gen_salt('bf'))::bytea, + 'super_admin', + TRUE, + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + FALSE, + NULL + ); -- Supported Operations -INSERT INTO supported_operations (id, name, description) VALUES -(1, 'SportBook', 'Sportbook operations'), -(2, 'Virtual', 'Virtual operations'); - +INSERT INTO supported_operations (id, name, description) +VALUES (1, 'SportBook', 'Sportbook operations'), + (2, 'Virtual', 'Virtual operations'); -- Wallets INSERT INTO wallets ( - id, balance, is_withdraw, is_bettable, is_transferable, user_id, - type, currency, is_active, created_at, updated_at -) VALUES -(1, 10000, TRUE, TRUE, TRUE, 1, 'regular', 'ETB', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), -(2, 5000, FALSE, TRUE, TRUE, 1, 'static', 'ETB', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), -(3, 20000, TRUE, TRUE, TRUE, 2, 'company_main', 'ETB', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), -(4, 15000, TRUE, TRUE, TRUE, 2, 'branch_main', 'ETB', TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); - + id, + balance, + is_withdraw, + is_bettable, + is_transferable, + user_id, + type, + currency, + is_active, + created_at, + updated_at + ) +VALUES ( + 1, + 10000, + TRUE, + TRUE, + TRUE, + 1, + 'regular', + 'ETB', + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP + ), + ( + 2, + 5000, + FALSE, + TRUE, + TRUE, + 1, + 'static', + 'ETB', + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP + ), + ( + 3, + 20000, + TRUE, + TRUE, + TRUE, + 2, + 'company_main', + 'ETB', + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP + ), + ( + 4, + 15000, + TRUE, + TRUE, + TRUE, + 2, + 'branch_main', + 'ETB', + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP + ); -- Customer Wallets -INSERT INTO customer_wallets (id, customer_id, regular_wallet_id, static_wallet_id) +INSERT INTO customer_wallets ( + id, + customer_id, + regular_wallet_id, + static_wallet_id + ) VALUES (1, 1, 1, 2); - -- Company INSERT INTO companies ( - id, name, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at -) VALUES -(1, 'Test Company', 2, 3, 0.10, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); - + id, + name, + slug + admin_id, + wallet_id, + deducted_percentage, + is_active, + created_at, + updated_at + ) +VALUES ( + 1, + 'FortuneBets', + 'fortunebets', + 2, + 3, + 0.10, + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP + ); -- Branch INSERT INTO branches ( - id, name, location, wallet_id, branch_manager_id, company_id, - is_self_owned, profit_percent, is_active, created_at, updated_at -) VALUES -(1, 'Test Branch', 'addis_ababa', 4, 2, 1, TRUE, 0.10, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); - -COMMIT; - + id, + name, + location, + wallet_id, + branch_manager_id, + company_id, + is_self_owned, + profit_percent, + is_active, + created_at, + updated_at + ) +VALUES ( + 1, + 'Test Branch', + 'addis_ababa', + 4, + 2, + 1, + TRUE, + 0.10, + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP + ); +COMMIT; \ No newline at end of file diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index fb57714..717d0d2 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -17,11 +17,14 @@ CREATE TABLE IF NOT EXISTS users ( CHECK ( email IS NOT NULL OR phone_number IS NOT NULL - ) + ), + UNIQUE(email, company_id), + UNIQUE (phone_number, company_id) ); CREATE TABLE IF NOT EXISTS wallets ( id BIGSERIAL PRIMARY KEY, balance BIGINT NOT NULL DEFAULT 0, + currency VARCHAR(3) NOT NULL DEFAULT 'ETB', is_withdraw BOOLEAN NOT NULL, is_bettable BOOLEAN NOT NULL, is_transferable BOOLEAN NOT NULL, @@ -54,6 +57,7 @@ CREATE TABLE otps ( ); CREATE TABLE IF NOT EXISTS bets ( id BIGSERIAL PRIMARY KEY, + company_id BIGINT NOT NULL, amount BIGINT NOT NULL, total_odds REAL NOT NULL, status INT NOT NULL, @@ -68,6 +72,7 @@ CREATE TABLE IF NOT EXISTS bets ( ); CREATE TABLE IF NOT EXISTS tickets ( id BIGSERIAL PRIMARY KEY, + company_id BIGINT NOT NULL, amount BIGINT NOT NULL, total_odds REAL NOT NULL, IP VARCHAR(255) NOT NULL, @@ -89,14 +94,14 @@ CREATE TABLE IF NOT EXISTS bet_outcomes ( sport_id BIGINT NOT NULL, event_id BIGINT NOT null, odd_id BIGINT NOT NULL, - home_team_name VARCHAR(255) NOT NULL, - away_team_name VARCHAR(255) NOT NULL, + home_team_name TEXT NOT NULL, + away_team_name TEXT NOT NULL, market_id BIGINT NOT NULL, - market_name VARCHAR(255) NOT NULL, + market_name TEXT NOT NULL, odd REAL NOT NULL, - odd_name VARCHAR(255) NOT NULL, - odd_header VARCHAR(255) NOT NULL, - odd_handicap VARCHAR(255) NOT NULL, + odd_name TEXT NOT NULL, + odd_header TEXT NOT NULL, + odd_handicap TEXT NOT NULL, status INT NOT NULL DEFAULT 0, expires TIMESTAMP NOT NULL ); @@ -105,14 +110,14 @@ CREATE TABLE IF NOT EXISTS ticket_outcomes ( ticket_id BIGINT NOT NULL, event_id BIGINT NOT null, odd_id BIGINT NOT NULL, - home_team_name VARCHAR(255) NOT NULL, - away_team_name VARCHAR(255) NOT NULL, + home_team_name TEXT NOT NULL, + away_team_name TEXT NOT NULL, market_id BIGINT NOT NULL, - market_name VARCHAR(255) NOT NULL, + market_name TEXT NOT NULL, odd REAL NOT NULL, - odd_name VARCHAR(255) NOT NULL, - odd_header VARCHAR(255) NOT NULL, - odd_handicap VARCHAR(255) NOT NULL, + odd_name TEXT NOT NULL, + odd_header TEXT NOT NULL, + odd_handicap TEXT NOT NULL, status INT NOT NULL DEFAULT 0, expires TIMESTAMP NOT NULL ); @@ -161,7 +166,7 @@ CREATE TABLE IF NOT EXISTS customer_wallets ( CREATE TABLE IF NOT EXISTS wallet_transfer ( id BIGSERIAL PRIMARY KEY, amount BIGINT, - message VARCHAR(255) NOT NULL, + message TEXT NOT NULL, type VARCHAR(255), receiver_wallet_id BIGINT, sender_wallet_id BIGINT, @@ -255,31 +260,39 @@ CREATE TABLE IF NOT EXISTS branch_locations ( ); CREATE TABLE events ( id TEXT PRIMARY KEY, - sport_id INT, - match_name TEXT, - home_team TEXT, - away_team TEXT, - home_team_id INT, - away_team_id INT, - home_kit_image TEXT, - away_kit_image TEXT, - league_id INT, - league_name TEXT, - league_cc TEXT, - start_time TIMESTAMP, + sport_id INT NOT NULL, + match_name TEXT NOT NULL, + home_team TEXT NOT NULL, + away_team TEXT NOT NULL, + home_team_id BIGINT NOT NULL, + away_team_id BIGINT NOT NULL, + home_kit_image TEXT NOT NULL, + away_kit_image TEXT NOT NULL, + league_id BIGINT NOT NULL, + league_name TEXT NOT NULL, + start_time TIMESTAMP NOT NULL, score TEXT, match_minute INT, timer_status TEXT, added_time INT, match_period INT, - is_live BOOLEAN, - status TEXT, + is_live BOOLEAN NOT NULL DEFAULT false, + status TEXT NOT NULL, fetched_at TIMESTAMP DEFAULT now(), - source TEXT DEFAULT 'b365api', - is_featured BOOLEAN NOT NULL DEFAULT FALSE, + source TEXT NOT NULL DEFAULT 'b365api' CHECK ( + 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, - winning_upper_limit INT NOT NULL, - is_active BOOLEAN NOT NULL DEFAULT TRUE + UNIQUE(source_event_id, source) ); CREATE TABLE event_history ( id BIGSERIAL PRIMARY KEY, @@ -287,52 +300,57 @@ CREATE TABLE event_history ( status TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -CREATE TABLE odds ( +CREATE TABLE company_event_settings ( id BIGSERIAL PRIMARY KEY, - event_id TEXT, - fi TEXT, + company_id BIGINT NOT NULL, + 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_name TEXT, - market_category TEXT, - market_id TEXT, - name TEXT, - handicap TEXT, - odds_value DOUBLE PRECISION, - section TEXT NOT NULL, - category TEXT, - raw_odds JSONB, + market_name TEXT NOT NULL, + market_category TEXT NOT NULL, + market_id TEXT NOT NULL, + raw_odds JSONB NOT NULL, + default_is_active BOOLEAN NOT NULL DEFAULT true, fetched_at TIMESTAMP DEFAULT now(), expires_at TIMESTAMP NOT NULL, - source TEXT DEFAULT 'b365api', - is_active BOOLEAN DEFAULT true, UNIQUE (market_id, name, handicap), UNIQUE (event_id, market_id, name, handicap), UNIQUE (event_id, market_id) ); CREATE TABLE odd_history ( 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, market_id TEXT NOT NULL, event_id TEXT NOT NULL, odd_value DOUBLE PRECISION NOT NULL, 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 ( 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, event_id TEXT NOT NULL, 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 ( id BIGSERIAL PRIMARY KEY, status_not_finished_count INT NOT NULL, @@ -352,6 +370,7 @@ CREATE TABLE result_log ( CREATE TABLE companies ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, + slug TEXT UNIQUE NOT NULL, admin_id BIGINT NOT NULL, wallet_id BIGINT NOT NULL, deducted_percentage REAL NOT NULL, @@ -366,19 +385,28 @@ CREATE TABLE companies ( CREATE TABLE leagues ( id BIGINT PRIMARY KEY, name TEXT NOT NULL, - img TEXT, + img_url TEXT, country_code TEXT, bet365_id INT, sport_id INT NOT NULL, - is_active BOOLEAN DEFAULT true, - is_featured BOOLEAN DEFAULT false + default_is_active BOOLEAN NOT NULL DEFAULT true, + 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 ( - id TEXT PRIMARY KEY, + id BIGSERIAL PRIMARY KEY, team_name TEXT NOT NULL, - country TEXT, - bet365_id INT, - logo_url TEXT + country_code TEXT NOT NULL, + bet365_id BIGINT, + img_url TEXT ); CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, @@ -387,14 +415,14 @@ CREATE TABLE IF NOT EXISTS settings ( updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE bonus ( - id BIGSERIAL PRIMARY KEY, multiplier REAL NOT NULL, + id BIGSERIAL PRIMARY KEY, balance_cap BIGINT NOT NULL DEFAULT 0 ); CREATE TABLE flags ( id BIGSERIAL PRIMARY KEY, 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, flagged_at TIMESTAMP DEFAULT NOW(), resolved BOOLEAN DEFAULT FALSE, @@ -544,17 +572,56 @@ SELECT sd.*, st.verified AS transaction_verified FROM shop_deposits AS sd 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 -ALTER TABLE users -ADD CONSTRAINT unique_email UNIQUE (email), - ADD CONSTRAINT unique_phone_number UNIQUE (phone_number); ALTER TABLE refresh_tokens ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users(id); ALTER TABLE bets ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users(id); ALTER TABLE wallets -ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users(id), - ADD COLUMN currency VARCHAR(3) NOT NULL DEFAULT 'ETB'; +ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users(id); ALTER TABLE customer_wallets ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users(id), ADD CONSTRAINT fk_customer_wallets_regular_wallet FOREIGN KEY (regular_wallet_id) REFERENCES wallets(id), @@ -584,4 +651,7 @@ ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(i ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE; ALTER TABLE companies ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id), - ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE; \ No newline at end of file + 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; \ No newline at end of file diff --git a/db/query/auth.sql b/db/query/auth.sql index 0444eff..3bd28bb 100644 --- a/db/query/auth.sql +++ b/db/query/auth.sql @@ -1,12 +1,17 @@ -- name: GetUserByEmailPhone :one SELECT * FROM users -WHERE email = $1 - OR phone_number = $2; +WHERE ( + email = $1 + OR phone_number = $2 + ) + AND ( + company_id = sqlc.narg('company_id') + OR sqlc.narg('company_id') IS NULL + ); -- name: CreateRefreshToken :exec INSERT INTO refresh_tokens (user_id, token, expires_at, created_at, revoked) VALUES ($1, $2, $3, $4, $5); - -- name: GetRefreshToken :one SELECT * FROM refresh_tokens diff --git a/db/query/company.sql b/db/query/company.sql index 32c7b2f..00128f5 100644 --- a/db/query/company.sql +++ b/db/query/company.sql @@ -1,11 +1,12 @@ -- name: CreateCompany :one INSERT INTO companies ( name, + slug, admin_id, wallet_id, deducted_percentage ) -VALUES ($1, $2, $3, $4) +VALUES ($1, $2, $3, $4, $5) RETURNING *; -- name: GetAllCompanies :many SELECT * @@ -29,6 +30,10 @@ WHERE ( SELECT * FROM companies_details WHERE id = $1; +-- name: GetCompanyIDUsingSlug :one +SELECT id +FROM companies +WHERE slug = $1; -- name: SearchCompanyByName :many SELECT * FROM companies_details diff --git a/db/query/custom_odds.sql b/db/query/custom_odds.sql index 199cda1..e4357e9 100644 --- a/db/query/custom_odds.sql +++ b/db/query/custom_odds.sql @@ -1,26 +1,35 @@ --- name: InsertCustomOdd :one -INSERT INTO custom_odd ( - odd_id, - raw_odd_id, - event_id, - odd_value - ) -VALUES ($1, $2, $3, $4) -RETURNING *; --- name: GetAllCustomOdds :many -SELECT * -FROM custom_odd; --- name: GetCustomOddByRawOddID :one -SELECT * -FROM custom_odd -WHERE raw_odd_id = $1; --- name: GetCustomOddByID :one -SELECT * -FROM custom_odd -WHERE id = $1; --- name: DeleteCustomOddsByID :exec -DELETE FROM disabled_odd -WHERE raw_odd_id = $1; --- name: DeleteCustomOddsByRawOddID :exec -DELETE FROM disabled_odd -WHERE raw_odd_id = $1; \ No newline at end of file +-- -- name: InsertCustomOddsMarket :one +-- INSERT INTO custom_odds_market ( +-- odds_market_id, +-- company_id, +-- event_id, +-- raw_odds +-- ) +-- VALUES ($1, $2, $3, $4) +-- RETURNING *; +-- -- name: GetAllCustomOdds :many +-- SELECT * +-- FROM custom_odds_market +-- WHERE ( +-- company_id = sqlc.narg('company_id') +-- OR sqlc.narg('company_id') IS NULL +-- ); +-- -- name: GetCustomOddByID :one +-- SELECT * +-- FROM custom_odds_market +-- WHERE id = $1; +-- -- name: GetCustomOddByOddID :one +-- SELECT * +-- FROM custom_odds_market +-- WHERE odds_market_id = $1 +-- AND company_id = $2; +-- -- 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; \ No newline at end of file diff --git a/db/query/disabled_odds.sql b/db/query/disabled_odds.sql index 9e328f1..d890f7d 100644 --- a/db/query/disabled_odds.sql +++ b/db/query/disabled_odds.sql @@ -1,10 +1,11 @@ -- name: InsertDisabledOdds :one INSERT INTO disabled_odd ( - odd_id, + odds_market_id, + company_id, event_id, raw_odd_id ) -VALUES ($1, $2, $3) +VALUES ($1, $2, $3, $4) RETURNING *; -- name: GetAllDisabledOdds :many SELECT * diff --git a/db/query/events.sql b/db/query/events.sql index 893081e..1a61445 100644 --- a/db/query/events.sql +++ b/db/query/events.sql @@ -11,13 +11,7 @@ INSERT INTO events ( away_kit_image, league_id, league_name, - league_cc, start_time, - score, - match_minute, - timer_status, - added_time, - match_period, is_live, status, source @@ -37,13 +31,7 @@ VALUES ( $12, $13, $14, - $15, - $16, - $17, - $18, - $19, - $20, - $21 + $15 ) ON CONFLICT (id) DO UPDATE SET sport_id = EXCLUDED.sport_id, @@ -64,79 +52,35 @@ SET sport_id = EXCLUDED.sport_id, added_time = EXCLUDED.added_time, match_period = EXCLUDED.match_period, is_live = EXCLUDED.is_live, - status = EXCLUDED.status, source = EXCLUDED.source, fetched_at = now(); --- name: InsertUpcomingEvent :exec -INSERT INTO events ( - id, - sport_id, - match_name, - home_team, - away_team, - home_team_id, - away_team_id, - home_kit_image, - away_kit_image, - league_id, - league_name, - league_cc, - start_time, - is_live, - status, - source +-- name: InsertEventSettings :exec +INSERT INTO company_event_settings ( + company_id, + event_id, + is_active, + is_featured, + winning_upper_limit ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - false, - 'upcoming', - $14 - ) ON CONFLICT (id) DO +VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO UPDATE -SET sport_id = EXCLUDED.sport_id, - match_name = EXCLUDED.match_name, - home_team = EXCLUDED.home_team, - 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(); +SET is_active = EXCLUDED.is_active, + is_featured = EXCLUDED.is_featured, + winning_upper_limit = EXCLUDED.winning_upper_limit; -- name: ListLiveEvents :many SELECT id -FROM events +FROM event_with_country WHERE is_live = true; -- name: GetAllUpcomingEvents :many SELECT * -FROM events +FROM event_with_country WHERE start_time > now() AND is_live = false AND status = 'upcoming' ORDER BY start_time ASC; --- name: GetExpiredUpcomingEvents :many -SELECT events.*, - leagues.country_code as league_cc -FROM events - LEFT JOIN leagues ON leagues.id = league_id +-- name: GetExpiredEvents :many +SELECT * +FROM event_with_country WHERE start_time < now() and ( status = sqlc.narg('status') @@ -145,8 +89,7 @@ WHERE start_time < now() ORDER BY start_time ASC; -- name: GetTotalEvents :one SELECT COUNT(*) -FROM events - LEFT JOIN leagues ON leagues.id = league_id +FROM event_with_country WHERE is_live = false AND status = 'upcoming' AND ( @@ -154,7 +97,7 @@ WHERE is_live = false OR sqlc.narg('league_id') IS NULL ) AND ( - events.sport_id = sqlc.narg('sport_id') + sport_id = sqlc.narg('sport_id') OR sqlc.narg('sport_id') IS NULL ) AND ( @@ -171,18 +114,12 @@ WHERE is_live = false OR sqlc.narg('first_start_time') IS NULL ) AND ( - leagues.country_code = sqlc.narg('country_code') + league_cc = sqlc.narg('country_code') 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 -SELECT events.*, - leagues.country_code as league_cc -FROM events - LEFT JOIN leagues ON leagues.id = league_id +SELECT * +FROM event_with_country WHERE start_time > now() AND is_live = false AND status = 'upcoming' @@ -191,7 +128,7 @@ WHERE start_time > now() OR sqlc.narg('league_id') IS NULL ) AND ( - events.sport_id = sqlc.narg('sport_id') + sport_id = sqlc.narg('sport_id') OR sqlc.narg('sport_id') IS NULL ) AND ( @@ -208,31 +145,96 @@ WHERE start_time > now() OR sqlc.narg('first_start_time') IS NULL ) AND ( - leagues.country_code = sqlc.narg('country_code') + league_cc = sqlc.narg('country_code') 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 ( - events.is_featured = sqlc.narg('is_featured') - OR sqlc.narg('is_featured') IS NULL + 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 + ); +-- 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 LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); -- name: GetUpcomingByID :one SELECT * -FROM events +FROM event_with_country WHERE id = $1 AND is_live = false AND status = 'upcoming' 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 UPDATE events SET score = $1, status = $2 WHERE id = $3; --- name: UpdateEventFeatured :exec -UPDATE events -SET is_featured = $1 -WHERE id = $2; -- name: IsEventMonitored :one SELECT is_monitored FROM events @@ -241,6 +243,19 @@ WHERE id = $1; UPDATE events SET is_monitored = $1 WHERE id = $2; +-- name: UpdateEventSettings :exec +UPDATE company_event_settings +SET is_active = COALESCE(sqlc.narg('is_active'), is_active), + is_featured = COALESCE( + sqlc.narg('is_featured'), + is_featured + ), + winning_upper_limit = COALESCE( + sqlc.narg('winning_upper_limit'), + winning_upper_limit + ) +WHERE event_id = $1 + AND company_id = $2; -- name: DeleteEvent :exec DELETE FROM events WHERE id = $1; \ No newline at end of file diff --git a/db/query/events_stat.sql b/db/query/events_stat.sql index 733969e..cf96ad5 100644 --- a/db/query/events_stat.sql +++ b/db/query/events_stat.sql @@ -4,20 +4,11 @@ SELECT DATE_TRUNC('month', start_time) AS month, FROM events JOIN leagues ON leagues.id = events.league_id 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') OR sqlc.narg('league_id') IS NULL ) GROUP BY month ORDER BY month; - -- name: GetLeagueEventStat :many SELECT leagues.id, leagues.name, @@ -63,9 +54,5 @@ SELECT leagues.id, ) AS removed FROM leagues 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, leagues.name; \ No newline at end of file diff --git a/db/query/flags.sql b/db/query/flags.sql index 60093c5..bb36e8b 100644 --- a/db/query/flags.sql +++ b/db/query/flags.sql @@ -1,7 +1,7 @@ -- name: CreateFlag :one INSERT INTO flags ( bet_id, - odd_id, + odds_market_id, reason ) VALUES ( $1, $2, $3 diff --git a/db/query/leagues.sql b/db/query/leagues.sql index 368da67..fbbc562 100644 --- a/db/query/leagues.sql +++ b/db/query/leagues.sql @@ -5,25 +5,28 @@ INSERT INTO leagues ( country_code, bet365_id, sport_id, - is_active, - is_featured + default_is_active, + default_is_featured ) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, country_code = EXCLUDED.country_code, bet365_id = EXCLUDED.bet365_id, - is_active = EXCLUDED.is_active, - is_featured = EXCLUDED.is_featured, sport_id = EXCLUDED.sport_id; +-- name: InsertLeagueSettings :exec +INSERT INTO company_league_settings ( + company_id, + 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; -- name: GetAllLeagues :many -SELECT id, - name, - country_code, - bet365_id, - is_active, - is_featured, - sport_id +SELECT * FROM leagues WHERE ( country_code = sqlc.narg('country_code') @@ -33,6 +36,20 @@ WHERE ( sport_id = sqlc.narg('sport_id') 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 ( is_active = sqlc.narg('is_active') OR sqlc.narg('is_active') IS NULL @@ -44,21 +61,12 @@ WHERE ( ORDER BY is_featured DESC, name ASC 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 SELECT EXISTS( SELECT 1 - FROM leagues - WHERE id = $1 + FROM company_league_settings + WHERE league_id = $1 + AND company_id = $2 AND is_active = true ); -- name: UpdateLeague :exec @@ -66,20 +74,14 @@ UPDATE leagues SET name = COALESCE(sqlc.narg('name'), name), country_code = COALESCE(sqlc.narg('country_code'), country_code), 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) WHERE id = $1; --- name: UpdateLeagueByBet365ID :exec -UPDATE leagues -SET name = COALESCE(sqlc.narg('name'), name), - id = COALESCE(sqlc.narg('id'), id), - country_code = COALESCE(sqlc.narg('country_code'), country_code), - 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) -WHERE bet365_id = $1; --- name: SetLeagueActive :exec -UPDATE leagues -SET is_active = $2 -WHERE id = $1; \ No newline at end of file +-- name: UpdateLeagueSettings :exec +UPDATE company_league_settings +SET is_active = COALESCE(sqlc.narg('is_active'), is_active), + is_featured = COALESCE( + sqlc.narg('is_featured'), + is_featured + ) +WHERE league_id = $1 + AND company_id = $2; \ No newline at end of file diff --git a/db/query/odd_history.sql b/db/query/odd_history.sql index f7368fc..b040c2e 100644 --- a/db/query/odd_history.sql +++ b/db/query/odd_history.sql @@ -1,6 +1,6 @@ -- name: InsertOddHistory :one INSERT INTO odd_history ( - odd_id, + odds_market_id, market_id, raw_odd_id, event_id, @@ -12,7 +12,7 @@ RETURNING *; SELECT * FROM odd_history WHERE ( - odd_id = sqlc.narg('odd_id') + odds_market_id = sqlc.narg('odd_id') OR sqlc.narg('odd_id') IS NULL ) AND ( @@ -39,7 +39,7 @@ WHERE ( SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) * FROM odd_history WHERE ( - odd_id = sqlc.narg('odd_id') + odds_market_id = sqlc.narg('odd_id') OR sqlc.narg('odd_id') IS NULL ) AND ( diff --git a/db/query/odds.sql b/db/query/odds.sql index 4699000..8c231b1 100644 --- a/db/query/odds.sql +++ b/db/query/odds.sql @@ -1,19 +1,11 @@ --- name: InsertNonLiveOdd :exec -INSERT INTO odds ( +-- name: InsertOddsMarket :exec +INSERT INTO odds_market ( event_id, - fi, market_type, market_name, market_category, market_id, - name, - handicap, - odds_value, - section, - category, raw_odds, - is_active, - source, fetched_at, expires_at ) @@ -25,64 +17,69 @@ VALUES ( $5, $6, $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15, - $16 + $8 ) ON CONFLICT (event_id, market_id) DO UPDATE -SET odds_value = EXCLUDED.odds_value, - raw_odds = EXCLUDED.raw_odds, - market_type = EXCLUDED.market_type, +SET market_type = EXCLUDED.market_type, market_name = EXCLUDED.market_name, market_category = EXCLUDED.market_category, - name = EXCLUDED.name, - handicap = EXCLUDED.handicap, + raw_odds = EXCLUDED.raw_odds, fetched_at = EXCLUDED.fetched_at, - is_active = EXCLUDED.is_active, - source = EXCLUDED.source, - fi = EXCLUDED.fi; --- name: GetPrematchOdds :many + expires_at = EXCLUDED.expires_at; +-- name: InsertOddSettings :exec +INSERT INTO company_odd_settings ( + company_id, + odds_market_id, + is_active, + custom_raw_odds + ) +VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO +UPDATE +SET is_active = EXCLUDED.is_active, + custom_raw_odds = EXCLUDED.custom_raw_odds; +-- name: GetAllOdds :many SELECT * -FROM odds -WHERE is_active = true - AND source = 'bet365'; --- name: GetALLPrematchOdds :many +FROM odds_market_with_event +LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); +-- name: GetAllOddsWithSettings :many SELECT * -FROM odds -WHERE is_active = true - AND source = 'bet365'; +FROM odds_market_with_settings +WHERE company_id = $1 +LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); -- name: GetOddsByMarketID :one SELECT * -FROM odds +FROM odds_market_with_event WHERE market_id = $1 - AND fi = $2 - AND is_active = true - AND source = 'bet365'; --- name: GetPrematchOddsByUpcomingID :many -SELECT o.* -FROM odds o - JOIN events e ON o.fi = e.id -WHERE e.id = $1 - AND e.is_live = false - AND e.status = 'upcoming' - AND o.is_active = true - AND o.source = 'bet365'; --- name: GetPaginatedPrematchOddsByUpcomingID :many -SELECT o.* -FROM odds o - JOIN events e ON o.fi = e.id -WHERE e.id = $1 - AND e.is_live = false - AND e.status = 'upcoming' - AND o.is_active = true - AND o.source = 'bet365' + AND event_id = $2; +-- name: GetOddsWithSettingsByMarketID :one +SELECT * +FROM odds_market_with_settings +WHERE market_id = $1 + AND event_id = $2 + AND company_id = $3; +-- name: GetOddsByEventID :many +SELECT * +FROM odds_market_with_event +WHERE event_id = $1 + AND ( + is_live = sqlc.narg('is_live') + OR sqlc.narg('is_live') IS NULL + ) + AND ( + status = sqlc.narg('status') + OR sqlc.narg('status') IS NULL + ) + AND ( + 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'); -- name: DeleteOddsForEvent :exec -DELETE FROM odds -Where fi = $1; \ No newline at end of file +DELETE FROM odds_market +Where event_id = $1; \ No newline at end of file diff --git a/db/query/user.sql b/db/query/user.sql index d7eae90..1b408cf 100644 --- a/db/query/user.sql +++ b/db/query/user.sql @@ -107,18 +107,15 @@ SELECT id, suspended_at, company_id FROM users -WHERE ( - first_name ILIKE '%' || $1 || '%' - OR last_name ILIKE '%' || $1 || '%' - OR phone_number LIKE '%' || $1 || '%' +WHERE (company_id = $1) + AND ( + first_name ILIKE '%' || $2 || '%' + OR last_name ILIKE '%' || $2 || '%' + OR phone_number LIKE '%' || $2 || '%' ) AND ( role = sqlc.narg('role') OR sqlc.narg('role') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL ); -- name: UpdateUser :exec UPDATE users @@ -146,12 +143,14 @@ SELECT EXISTS ( FROM users WHERE users.phone_number = $1 AND users.phone_number IS NOT NULL + AND users.company_id = $2 ) AS phone_exists, EXISTS ( SELECT 1 FROM users - WHERE users.email = $2 + WHERE users.email = $3 AND users.email IS NOT NULL + AND users.company_id = $2 ) AS email_exists; -- name: GetUserByEmail :one SELECT id, @@ -168,7 +167,8 @@ SELECT id, suspended_at, company_id FROM users -WHERE email = $1; +WHERE email = $1 + AND company_id = $2; -- name: GetUserByPhone :one SELECT id, first_name, @@ -184,7 +184,8 @@ SELECT id, suspended_at, company_id FROM users -WHERE phone_number = $1; +WHERE phone_number = $1 + AND company_id = $2; -- name: UpdatePassword :exec UPDATE users SET password = $1, @@ -192,6 +193,7 @@ SET password = $1, WHERE ( email = $2 OR phone_number = $3 + AND company_id = $4 ); -- name: GetAdminByCompanyID :one SELECT users.* diff --git a/gen/db/auth.sql.go b/gen/db/auth.sql.go index 9c55b29..1817514 100644 --- a/gen/db/auth.sql.go +++ b/gen/db/auth.sql.go @@ -78,17 +78,24 @@ func (q *Queries) GetRefreshTokenByUserID(ctx context.Context, userID int64) (Re const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by FROM users -WHERE email = $1 - OR phone_number = $2 +WHERE ( + email = $1 + OR phone_number = $2 + ) + AND ( + company_id = $3 + OR $3 IS NULL + ) ` type GetUserByEmailPhoneParams struct { Email pgtype.Text `json:"email"` PhoneNumber pgtype.Text `json:"phone_number"` + CompanyID pgtype.Int8 `json:"company_id"` } 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 err := row.Scan( &i.ID, diff --git a/gen/db/bet.sql.go b/gen/db/bet.sql.go index 31ca511..29d1e10 100644 --- a/gen/db/bet.sql.go +++ b/gen/db/bet.sql.go @@ -22,7 +22,7 @@ INSERT INTO bets ( fast_code ) 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 { @@ -48,6 +48,7 @@ func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, erro var i Bet err := row.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Status, @@ -100,7 +101,7 @@ func (q *Queries) DeleteBetOutcome(ctx context.Context, betID int64) error { } 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 wHERE ( user_id = $1 @@ -156,6 +157,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi var i BetWithOutcome if err := rows.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Status, @@ -182,7 +184,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi } 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 WHERE fast_code = $1 LIMIT 1 @@ -193,6 +195,7 @@ func (q *Queries) GetBetByFastCode(ctx context.Context, fastCode string) (BetWit var i BetWithOutcome err := row.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Status, @@ -212,7 +215,7 @@ func (q *Queries) GetBetByFastCode(ctx context.Context, fastCode string) (BetWit } 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 WHERE id = $1 ` @@ -222,6 +225,7 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err var i BetWithOutcome err := row.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Status, @@ -241,7 +245,7 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err } 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 WHERE user_id = $1 ` @@ -257,6 +261,7 @@ func (q *Queries) GetBetByUserID(ctx context.Context, userID int64) ([]BetWithOu var i BetWithOutcome if err := rows.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Status, @@ -424,7 +429,7 @@ func (q *Queries) GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (i } 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 WHERE status = 2 AND processed = false @@ -441,6 +446,7 @@ func (q *Queries) GetBetsForCashback(ctx context.Context) ([]BetWithOutcome, err var i BetWithOutcome if err := rows.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Status, diff --git a/gen/db/company.sql.go b/gen/db/company.sql.go index ac5980a..506eaca 100644 --- a/gen/db/company.sql.go +++ b/gen/db/company.sql.go @@ -14,16 +14,18 @@ import ( const CreateCompany = `-- name: CreateCompany :one INSERT INTO companies ( name, + slug, admin_id, wallet_id, deducted_percentage ) -VALUES ($1, $2, $3, $4) -RETURNING id, name, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at +VALUES ($1, $2, $3, $4, $5) +RETURNING id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at ` type CreateCompanyParams struct { Name string `json:"name"` + Slug string `json:"slug"` AdminID int64 `json:"admin_id"` WalletID int64 `json:"wallet_id"` DeductedPercentage float32 `json:"deducted_percentage"` @@ -32,6 +34,7 @@ type CreateCompanyParams struct { func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (Company, error) { row := q.db.QueryRow(ctx, CreateCompany, arg.Name, + arg.Slug, arg.AdminID, arg.WalletID, arg.DeductedPercentage, @@ -40,6 +43,7 @@ func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (C err := row.Scan( &i.ID, &i.Name, + &i.Slug, &i.AdminID, &i.WalletID, &i.DeductedPercentage, @@ -61,7 +65,7 @@ func (q *Queries) DeleteCompany(ctx context.Context, id int64) error { } 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 WHERE ( name ILIKE '%' || $1 || '%' @@ -98,6 +102,7 @@ func (q *Queries) GetAllCompanies(ctx context.Context, arg GetAllCompaniesParams if err := rows.Scan( &i.ID, &i.Name, + &i.Slug, &i.AdminID, &i.WalletID, &i.DeductedPercentage, @@ -121,7 +126,7 @@ func (q *Queries) GetAllCompanies(ctx context.Context, arg GetAllCompaniesParams } 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 WHERE id = $1 ` @@ -132,6 +137,7 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail err := row.Scan( &i.ID, &i.Name, + &i.Slug, &i.AdminID, &i.WalletID, &i.DeductedPercentage, @@ -147,8 +153,21 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail 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 -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 WHERE name ILIKE '%' || $1 || '%' ` @@ -165,6 +184,7 @@ func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text) if err := rows.Scan( &i.ID, &i.Name, + &i.Slug, &i.AdminID, &i.WalletID, &i.DeductedPercentage, @@ -198,7 +218,7 @@ SET name = COALESCE($2, name), ), updated_at = CURRENT_TIMESTAMP 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 { @@ -221,6 +241,7 @@ func (q *Queries) UpdateCompany(ctx context.Context, arg UpdateCompanyParams) (C err := row.Scan( &i.ID, &i.Name, + &i.Slug, &i.AdminID, &i.WalletID, &i.DeductedPercentage, diff --git a/gen/db/custom_odds.sql.go b/gen/db/custom_odds.sql.go index 9e84561..efc1f02 100644 --- a/gen/db/custom_odds.sql.go +++ b/gen/db/custom_odds.sql.go @@ -7,48 +7,70 @@ package dbgen import ( "context" + + "github.com/jackc/pgx/v5/pgtype" ) -const DeleteCustomOddsByID = `-- name: DeleteCustomOddsByID :exec -DELETE FROM disabled_odd -WHERE raw_odd_id = $1 +const DeleteCustomOddByEventID = `-- name: DeleteCustomOddByEventID :exec +DELETE FROM custom_odds_market +WHERE event_id = $1 ` -func (q *Queries) DeleteCustomOddsByID(ctx context.Context, rawOddID int64) error { - _, err := q.db.Exec(ctx, DeleteCustomOddsByID, rawOddID) +func (q *Queries) DeleteCustomOddByEventID(ctx context.Context, eventID string) error { + _, err := q.db.Exec(ctx, DeleteCustomOddByEventID, eventID) return err } -const DeleteCustomOddsByRawOddID = `-- name: DeleteCustomOddsByRawOddID :exec -DELETE FROM disabled_odd -WHERE raw_odd_id = $1 +const DeleteCustomOddsByID = `-- name: DeleteCustomOddsByID :exec +DELETE FROM custom_odds_market +WHERE id = $1 ` -func (q *Queries) DeleteCustomOddsByRawOddID(ctx context.Context, rawOddID int64) error { - _, err := q.db.Exec(ctx, DeleteCustomOddsByRawOddID, rawOddID) +func (q *Queries) DeleteCustomOddsByID(ctx context.Context, id int64) error { + _, 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 } const GetAllCustomOdds = `-- name: GetAllCustomOdds :many -SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at -FROM custom_odd +SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at +FROM custom_odds_market +WHERE ( + company_id = $1 + OR $1 IS NULL + ) ` -func (q *Queries) GetAllCustomOdds(ctx context.Context) ([]CustomOdd, error) { - rows, err := q.db.Query(ctx, GetAllCustomOdds) +func (q *Queries) GetAllCustomOdds(ctx context.Context, companyID pgtype.Int8) ([]CustomOddsMarket, error) { + rows, err := q.db.Query(ctx, GetAllCustomOdds, companyID) if err != nil { return nil, err } defer rows.Close() - var items []CustomOdd + var items []CustomOddsMarket for rows.Next() { - var i CustomOdd + var i CustomOddsMarket if err := rows.Scan( &i.ID, - &i.OddID, - &i.RawOddID, + &i.CompanyID, + &i.OddsMarketID, &i.EventID, - &i.OddValue, + &i.RawOdds, &i.CreatedAt, ); err != nil { return nil, err @@ -62,77 +84,83 @@ func (q *Queries) GetAllCustomOdds(ctx context.Context) ([]CustomOdd, error) { } const GetCustomOddByID = `-- name: GetCustomOddByID :one -SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at -FROM custom_odd +SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at +FROM custom_odds_market 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) - var i CustomOdd + var i CustomOddsMarket err := row.Scan( &i.ID, - &i.OddID, - &i.RawOddID, + &i.CompanyID, + &i.OddsMarketID, &i.EventID, - &i.OddValue, + &i.RawOdds, &i.CreatedAt, ) return i, err } -const GetCustomOddByRawOddID = `-- name: GetCustomOddByRawOddID :one -SELECT id, odd_id, raw_odd_id, event_id, odd_value, created_at -FROM custom_odd -WHERE raw_odd_id = $1 +const GetCustomOddByOddID = `-- name: GetCustomOddByOddID :one +SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at +FROM custom_odds_market +WHERE odds_market_id = $1 + AND company_id = $2 ` -func (q *Queries) GetCustomOddByRawOddID(ctx context.Context, rawOddID int64) (CustomOdd, error) { - row := q.db.QueryRow(ctx, GetCustomOddByRawOddID, rawOddID) - var i CustomOdd +type GetCustomOddByOddIDParams struct { + OddsMarketID int64 `json:"odds_market_id"` + 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( &i.ID, - &i.OddID, - &i.RawOddID, + &i.CompanyID, + &i.OddsMarketID, &i.EventID, - &i.OddValue, + &i.RawOdds, &i.CreatedAt, ) return i, err } -const InsertCustomOdd = `-- name: InsertCustomOdd :one -INSERT INTO custom_odd ( - odd_id, - raw_odd_id, +const InsertCustomOddsMarket = `-- name: InsertCustomOddsMarket :one +INSERT INTO custom_odds_market ( + odds_market_id, + company_id, event_id, - odd_value + raw_odds ) 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 { - OddID int64 `json:"odd_id"` - RawOddID int64 `json:"raw_odd_id"` - EventID string `json:"event_id"` - OddValue float64 `json:"odd_value"` +type InsertCustomOddsMarketParams struct { + OddsMarketID int64 `json:"odds_market_id"` + CompanyID int64 `json:"company_id"` + EventID string `json:"event_id"` + RawOdds []byte `json:"raw_odds"` } -func (q *Queries) InsertCustomOdd(ctx context.Context, arg InsertCustomOddParams) (CustomOdd, error) { - row := q.db.QueryRow(ctx, InsertCustomOdd, - arg.OddID, - arg.RawOddID, +func (q *Queries) InsertCustomOddsMarket(ctx context.Context, arg InsertCustomOddsMarketParams) (CustomOddsMarket, error) { + row := q.db.QueryRow(ctx, InsertCustomOddsMarket, + arg.OddsMarketID, + arg.CompanyID, arg.EventID, - arg.OddValue, + arg.RawOdds, ) - var i CustomOdd + var i CustomOddsMarket err := row.Scan( &i.ID, - &i.OddID, - &i.RawOddID, + &i.CompanyID, + &i.OddsMarketID, &i.EventID, - &i.OddValue, + &i.RawOdds, &i.CreatedAt, ) return i, err diff --git a/gen/db/disabled_odds.sql.go b/gen/db/disabled_odds.sql.go index 19fa041..85dcd2e 100644 --- a/gen/db/disabled_odds.sql.go +++ b/gen/db/disabled_odds.sql.go @@ -30,7 +30,7 @@ func (q *Queries) DeleteDisabledOddsByRawOddID(ctx context.Context, rawOddID int } 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 ` @@ -45,7 +45,8 @@ func (q *Queries) GetAllDisabledOdds(ctx context.Context) ([]DisabledOdd, error) var i DisabledOdd if err := rows.Scan( &i.ID, - &i.OddID, + &i.CompanyID, + &i.OddsMarketID, &i.RawOddID, &i.EventID, &i.CreatedAt, @@ -61,7 +62,7 @@ func (q *Queries) GetAllDisabledOdds(ctx context.Context) ([]DisabledOdd, error) } 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 WHERE raw_odd_id = $1 ` @@ -71,7 +72,8 @@ func (q *Queries) GetDisabledOddByID(ctx context.Context, rawOddID int64) (Disab var i DisabledOdd err := row.Scan( &i.ID, - &i.OddID, + &i.CompanyID, + &i.OddsMarketID, &i.RawOddID, &i.EventID, &i.CreatedAt, @@ -80,7 +82,7 @@ func (q *Queries) GetDisabledOddByID(ctx context.Context, rawOddID int64) (Disab } 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 WHERE raw_odd_id = $1 ` @@ -90,7 +92,8 @@ func (q *Queries) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) var i DisabledOdd err := row.Scan( &i.ID, - &i.OddID, + &i.CompanyID, + &i.OddsMarketID, &i.RawOddID, &i.EventID, &i.CreatedAt, @@ -100,26 +103,34 @@ func (q *Queries) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) const InsertDisabledOdds = `-- name: InsertDisabledOdds :one INSERT INTO disabled_odd ( - odd_id, + odds_market_id, + company_id, event_id, raw_odd_id ) -VALUES ($1, $2, $3) -RETURNING id, odd_id, raw_odd_id, event_id, created_at +VALUES ($1, $2, $3, $4) +RETURNING id, company_id, odds_market_id, raw_odd_id, event_id, created_at ` type InsertDisabledOddsParams struct { - OddID int64 `json:"odd_id"` - EventID string `json:"event_id"` - RawOddID int64 `json:"raw_odd_id"` + OddsMarketID int64 `json:"odds_market_id"` + CompanyID int64 `json:"company_id"` + EventID string `json:"event_id"` + RawOddID int64 `json:"raw_odd_id"` } 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 err := row.Scan( &i.ID, - &i.OddID, + &i.CompanyID, + &i.OddsMarketID, &i.RawOddID, &i.EventID, &i.CreatedAt, diff --git a/gen/db/events.sql.go b/gen/db/events.sql.go index 8e1978a..313b240 100644 --- a/gen/db/events.sql.go +++ b/gen/db/events.sql.go @@ -22,23 +22,23 @@ func (q *Queries) DeleteEvent(ctx context.Context, id string) error { } const GetAllUpcomingEvents = `-- name: GetAllUpcomingEvents :many -SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, league_cc, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, is_featured, is_monitored, winning_upper_limit, is_active -FROM events +SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc +FROM event_with_country WHERE start_time > now() AND is_live = false AND status = 'upcoming' ORDER BY start_time ASC ` -func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]Event, error) { +func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]EventWithCountry, error) { rows, err := q.db.Query(ctx, GetAllUpcomingEvents) if err != nil { return nil, err } defer rows.Close() - var items []Event + var items []EventWithCountry for rows.Next() { - var i Event + var i EventWithCountry if err := rows.Scan( &i.ID, &i.SportID, @@ -51,7 +51,6 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]Event, error) { &i.AwayKitImage, &i.LeagueID, &i.LeagueName, - &i.LeagueCc, &i.StartTime, &i.Score, &i.MatchMinute, @@ -62,10 +61,11 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]Event, error) { &i.Status, &i.FetchedAt, &i.Source, - &i.IsFeatured, + &i.DefaultIsActive, + &i.DefaultIsFeatured, + &i.DefaultWinningUpperLimit, &i.IsMonitored, - &i.WinningUpperLimit, - &i.IsActive, + &i.LeagueCc, ); err != nil { return nil, err } @@ -77,312 +77,24 @@ func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]Event, error) { return items, nil } -const GetExpiredUpcomingEvents = `-- name: GetExpiredUpcomingEvents :many -SELECT events.id, events.sport_id, events.match_name, events.home_team, events.away_team, events.home_team_id, events.away_team_id, events.home_kit_image, events.away_kit_image, events.league_id, events.league_name, events.league_cc, events.start_time, events.score, events.match_minute, events.timer_status, events.added_time, events.match_period, events.is_live, events.status, events.fetched_at, events.source, events.is_featured, events.is_monitored, events.winning_upper_limit, events.is_active, - leagues.country_code as league_cc -FROM events - LEFT JOIN leagues ON leagues.id = league_id -WHERE start_time < now() - and ( - status = $1 - OR $1 IS NULL - ) -ORDER BY start_time ASC -` - -type GetExpiredUpcomingEventsRow struct { - ID string `json:"id"` - SportID pgtype.Int4 `json:"sport_id"` - MatchName pgtype.Text `json:"match_name"` - HomeTeam pgtype.Text `json:"home_team"` - AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Int4 `json:"home_team_id"` - AwayTeamID pgtype.Int4 `json:"away_team_id"` - HomeKitImage pgtype.Text `json:"home_kit_image"` - AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Int4 `json:"league_id"` - LeagueName pgtype.Text `json:"league_name"` - LeagueCc pgtype.Text `json:"league_cc"` - 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 pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - Source pgtype.Text `json:"source"` - IsFeatured bool `json:"is_featured"` - IsMonitored bool `json:"is_monitored"` - WinningUpperLimit int32 `json:"winning_upper_limit"` - IsActive bool `json:"is_active"` - LeagueCc_2 pgtype.Text `json:"league_cc_2"` -} - -func (q *Queries) GetExpiredUpcomingEvents(ctx context.Context, status pgtype.Text) ([]GetExpiredUpcomingEventsRow, error) { - rows, err := q.db.Query(ctx, GetExpiredUpcomingEvents, status) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetExpiredUpcomingEventsRow - for rows.Next() { - var i GetExpiredUpcomingEventsRow - if err := rows.Scan( - &i.ID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.LeagueCc, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.Source, - &i.IsFeatured, - &i.IsMonitored, - &i.WinningUpperLimit, - &i.IsActive, - &i.LeagueCc_2, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetPaginatedUpcomingEvents = `-- name: GetPaginatedUpcomingEvents :many -SELECT events.id, events.sport_id, events.match_name, events.home_team, events.away_team, events.home_team_id, events.away_team_id, events.home_kit_image, events.away_kit_image, events.league_id, events.league_name, events.league_cc, events.start_time, events.score, events.match_minute, events.timer_status, events.added_time, events.match_period, events.is_live, events.status, events.fetched_at, events.source, events.is_featured, events.is_monitored, events.winning_upper_limit, events.is_active, - leagues.country_code as league_cc -FROM events - LEFT JOIN leagues ON leagues.id = league_id -WHERE start_time > now() - AND is_live = false - AND status = 'upcoming' - AND ( - league_id = $1 - OR $1 IS NULL - ) - AND ( - events.sport_id = $2 - OR $2 IS NULL - ) - AND ( - match_name ILIKE '%' || $3 || '%' - OR league_name ILIKE '%' || $3 || '%' - OR $3 IS NULL - ) - AND ( - start_time < $4 - OR $4 IS NULL - ) - AND ( - start_time > $5 - OR $5 IS NULL - ) - AND ( - leagues.country_code = $6 - OR $6 IS NULL - ) - AND ( - events.is_featured = $7 - OR $7 IS NULL - ) -ORDER BY start_time ASC -LIMIT $9 OFFSET $8 -` - -type GetPaginatedUpcomingEventsParams struct { - LeagueID pgtype.Int4 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - LastStartTime pgtype.Timestamp `json:"last_start_time"` - FirstStartTime pgtype.Timestamp `json:"first_start_time"` - CountryCode pgtype.Text `json:"country_code"` - IsFeatured pgtype.Bool `json:"is_featured"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -type GetPaginatedUpcomingEventsRow struct { - ID string `json:"id"` - SportID pgtype.Int4 `json:"sport_id"` - MatchName pgtype.Text `json:"match_name"` - HomeTeam pgtype.Text `json:"home_team"` - AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Int4 `json:"home_team_id"` - AwayTeamID pgtype.Int4 `json:"away_team_id"` - HomeKitImage pgtype.Text `json:"home_kit_image"` - AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Int4 `json:"league_id"` - LeagueName pgtype.Text `json:"league_name"` - LeagueCc pgtype.Text `json:"league_cc"` - 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 pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - Source pgtype.Text `json:"source"` - IsFeatured bool `json:"is_featured"` - IsMonitored bool `json:"is_monitored"` - WinningUpperLimit int32 `json:"winning_upper_limit"` - IsActive bool `json:"is_active"` - LeagueCc_2 pgtype.Text `json:"league_cc_2"` -} - -func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginatedUpcomingEventsParams) ([]GetPaginatedUpcomingEventsRow, error) { - rows, err := q.db.Query(ctx, GetPaginatedUpcomingEvents, - arg.LeagueID, - arg.SportID, - arg.Query, - arg.LastStartTime, - arg.FirstStartTime, - arg.CountryCode, - arg.IsFeatured, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetPaginatedUpcomingEventsRow - for rows.Next() { - var i GetPaginatedUpcomingEventsRow - if err := rows.Scan( - &i.ID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.LeagueCc, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.Source, - &i.IsFeatured, - &i.IsMonitored, - &i.WinningUpperLimit, - &i.IsActive, - &i.LeagueCc_2, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTotalEvents = `-- name: GetTotalEvents :one -SELECT COUNT(*) -FROM events - LEFT JOIN leagues ON leagues.id = league_id -WHERE is_live = false - AND status = 'upcoming' - AND ( - league_id = $1 - OR $1 IS NULL - ) - AND ( - events.sport_id = $2 - OR $2 IS NULL - ) - AND ( - match_name ILIKE '%' || $3 || '%' - OR league_name ILIKE '%' || $3 || '%' - OR $3 IS NULL - ) - AND ( - start_time < $4 - OR $4 IS NULL - ) - AND ( - start_time > $5 - OR $5 IS NULL - ) - AND ( - leagues.country_code = $6 - OR $6 IS NULL - ) - AND ( - events.is_featured = $7 - OR $7 IS NULL - ) -` - -type GetTotalEventsParams struct { - LeagueID pgtype.Int4 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - LastStartTime pgtype.Timestamp `json:"last_start_time"` - FirstStartTime pgtype.Timestamp `json:"first_start_time"` - CountryCode pgtype.Text `json:"country_code"` - IsFeatured pgtype.Bool `json:"is_featured"` -} - -func (q *Queries) GetTotalEvents(ctx context.Context, arg GetTotalEventsParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalEvents, - arg.LeagueID, - arg.SportID, - arg.Query, - arg.LastStartTime, - arg.FirstStartTime, - arg.CountryCode, - arg.IsFeatured, - ) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetUpcomingByID = `-- name: GetUpcomingByID :one -SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, league_cc, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, is_featured, is_monitored, winning_upper_limit, is_active -FROM events +const GetEventWithSettingByID = `-- name: GetEventWithSettingByID :one +SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, company_id, is_active, is_featured, winning_upper_limit, updated_at, league_cc +FROM event_with_settings WHERE id = $1 + AND company_id = $2 AND is_live = false AND status = 'upcoming' LIMIT 1 ` -func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (Event, error) { - row := q.db.QueryRow(ctx, GetUpcomingByID, id) - var i Event +type GetEventWithSettingByIDParams struct { + ID string `json:"id"` + CompanyID int64 `json:"company_id"` +} + +func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithSettingByIDParams) (EventWithSetting, error) { + row := q.db.QueryRow(ctx, GetEventWithSettingByID, arg.ID, arg.CompanyID) + var i EventWithSetting err := row.Scan( &i.ID, &i.SportID, @@ -395,7 +107,6 @@ func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (Event, error) &i.AwayKitImage, &i.LeagueID, &i.LeagueName, - &i.LeagueCc, &i.StartTime, &i.Score, &i.MatchMinute, @@ -406,10 +117,442 @@ func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (Event, error) &i.Status, &i.FetchedAt, &i.Source, - &i.IsFeatured, + &i.DefaultIsActive, + &i.DefaultIsFeatured, + &i.DefaultWinningUpperLimit, &i.IsMonitored, - &i.WinningUpperLimit, + &i.CompanyID, &i.IsActive, + &i.IsFeatured, + &i.WinningUpperLimit, + &i.UpdatedAt, + &i.LeagueCc, + ) + return i, err +} + +const GetEventsWithSettings = `-- name: GetEventsWithSettings :many +SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, company_id, is_active, is_featured, winning_upper_limit, updated_at, league_cc +FROM event_with_settings +WHERE company_id = $1 + AND start_time > now() + AND is_live = false + AND status = 'upcoming' + AND ( + league_id = $2 + OR $2 IS NULL + ) + AND ( + sport_id = $3 + OR $3 IS NULL + ) + AND ( + match_name ILIKE '%' || $4 || '%' + OR league_name ILIKE '%' || $4 || '%' + OR $4 IS NULL + ) + AND ( + start_time < $5 + OR $5 IS NULL + ) + AND ( + start_time > $6 + OR $6 IS NULL + ) + AND ( + league_cc = $7 + OR $7 IS NULL + ) +ORDER BY start_time ASC +LIMIT $9 OFFSET $8 +` + +type GetEventsWithSettingsParams struct { + CompanyID int64 `json:"company_id"` + LeagueID pgtype.Int8 `json:"league_id"` + SportID pgtype.Int4 `json:"sport_id"` + Query pgtype.Text `json:"query"` + LastStartTime pgtype.Timestamp `json:"last_start_time"` + FirstStartTime pgtype.Timestamp `json:"first_start_time"` + CountryCode pgtype.Text `json:"country_code"` + Offset pgtype.Int4 `json:"offset"` + Limit pgtype.Int4 `json:"limit"` +} + +func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSettingsParams) ([]EventWithSetting, error) { + rows, err := q.db.Query(ctx, GetEventsWithSettings, + arg.CompanyID, + arg.LeagueID, + arg.SportID, + arg.Query, + arg.LastStartTime, + arg.FirstStartTime, + arg.CountryCode, + arg.Offset, + arg.Limit, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []EventWithSetting + for rows.Next() { + var i EventWithSetting + if err := rows.Scan( + &i.ID, + &i.SportID, + &i.MatchName, + &i.HomeTeam, + &i.AwayTeam, + &i.HomeTeamID, + &i.AwayTeamID, + &i.HomeKitImage, + &i.AwayKitImage, + &i.LeagueID, + &i.LeagueName, + &i.StartTime, + &i.Score, + &i.MatchMinute, + &i.TimerStatus, + &i.AddedTime, + &i.MatchPeriod, + &i.IsLive, + &i.Status, + &i.FetchedAt, + &i.Source, + &i.DefaultIsActive, + &i.DefaultIsFeatured, + &i.DefaultWinningUpperLimit, + &i.IsMonitored, + &i.CompanyID, + &i.IsActive, + &i.IsFeatured, + &i.WinningUpperLimit, + &i.UpdatedAt, + &i.LeagueCc, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const GetExpiredEvents = `-- name: GetExpiredEvents :many +SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc +FROM event_with_country +WHERE start_time < now() + and ( + status = $1 + OR $1 IS NULL + ) +ORDER BY start_time ASC +` + +func (q *Queries) GetExpiredEvents(ctx context.Context, status pgtype.Text) ([]EventWithCountry, error) { + rows, err := q.db.Query(ctx, GetExpiredEvents, status) + if err != nil { + return nil, err + } + defer rows.Close() + var items []EventWithCountry + for rows.Next() { + var i EventWithCountry + if err := rows.Scan( + &i.ID, + &i.SportID, + &i.MatchName, + &i.HomeTeam, + &i.AwayTeam, + &i.HomeTeamID, + &i.AwayTeamID, + &i.HomeKitImage, + &i.AwayKitImage, + &i.LeagueID, + &i.LeagueName, + &i.StartTime, + &i.Score, + &i.MatchMinute, + &i.TimerStatus, + &i.AddedTime, + &i.MatchPeriod, + &i.IsLive, + &i.Status, + &i.FetchedAt, + &i.Source, + &i.DefaultIsActive, + &i.DefaultIsFeatured, + &i.DefaultWinningUpperLimit, + &i.IsMonitored, + &i.LeagueCc, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const GetPaginatedUpcomingEvents = `-- name: GetPaginatedUpcomingEvents :many +SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc +FROM event_with_country +WHERE start_time > now() + AND is_live = false + AND status = 'upcoming' + AND ( + league_id = $1 + OR $1 IS NULL + ) + AND ( + sport_id = $2 + OR $2 IS NULL + ) + AND ( + match_name ILIKE '%' || $3 || '%' + OR league_name ILIKE '%' || $3 || '%' + OR $3 IS NULL + ) + AND ( + start_time < $4 + OR $4 IS NULL + ) + AND ( + start_time > $5 + OR $5 IS NULL + ) + AND ( + league_cc = $6 + OR $6 IS NULL + ) +ORDER BY start_time ASC +LIMIT $8 OFFSET $7 +` + +type GetPaginatedUpcomingEventsParams struct { + LeagueID pgtype.Int8 `json:"league_id"` + SportID pgtype.Int4 `json:"sport_id"` + Query pgtype.Text `json:"query"` + LastStartTime pgtype.Timestamp `json:"last_start_time"` + FirstStartTime pgtype.Timestamp `json:"first_start_time"` + CountryCode pgtype.Text `json:"country_code"` + Offset pgtype.Int4 `json:"offset"` + Limit pgtype.Int4 `json:"limit"` +} + +func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginatedUpcomingEventsParams) ([]EventWithCountry, error) { + rows, err := q.db.Query(ctx, GetPaginatedUpcomingEvents, + arg.LeagueID, + arg.SportID, + arg.Query, + arg.LastStartTime, + arg.FirstStartTime, + arg.CountryCode, + arg.Offset, + arg.Limit, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []EventWithCountry + for rows.Next() { + var i EventWithCountry + if err := rows.Scan( + &i.ID, + &i.SportID, + &i.MatchName, + &i.HomeTeam, + &i.AwayTeam, + &i.HomeTeamID, + &i.AwayTeamID, + &i.HomeKitImage, + &i.AwayKitImage, + &i.LeagueID, + &i.LeagueName, + &i.StartTime, + &i.Score, + &i.MatchMinute, + &i.TimerStatus, + &i.AddedTime, + &i.MatchPeriod, + &i.IsLive, + &i.Status, + &i.FetchedAt, + &i.Source, + &i.DefaultIsActive, + &i.DefaultIsFeatured, + &i.DefaultWinningUpperLimit, + &i.IsMonitored, + &i.LeagueCc, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const GetTotalCompanyEvents = `-- name: GetTotalCompanyEvents :one +SELECT COUNT(*) +FROM event_with_settings +WHERE company_id = $1 + AND is_live = false + AND status = 'upcoming' + AND ( + league_id = $2 + OR $2 IS NULL + ) + AND ( + sport_id = $3 + OR $3 IS NULL + ) + AND ( + match_name ILIKE '%' || $4 || '%' + OR league_name ILIKE '%' || $4 || '%' + OR $4 IS NULL + ) + AND ( + start_time < $5 + OR $5 IS NULL + ) + AND ( + start_time > $6 + OR $6 IS NULL + ) + AND ( + league_cc = $7 + OR $7 IS NULL + ) +` + +type GetTotalCompanyEventsParams struct { + CompanyID int64 `json:"company_id"` + LeagueID pgtype.Int8 `json:"league_id"` + SportID pgtype.Int4 `json:"sport_id"` + Query pgtype.Text `json:"query"` + LastStartTime pgtype.Timestamp `json:"last_start_time"` + FirstStartTime pgtype.Timestamp `json:"first_start_time"` + CountryCode pgtype.Text `json:"country_code"` +} + +func (q *Queries) GetTotalCompanyEvents(ctx context.Context, arg GetTotalCompanyEventsParams) (int64, error) { + row := q.db.QueryRow(ctx, GetTotalCompanyEvents, + arg.CompanyID, + arg.LeagueID, + arg.SportID, + arg.Query, + arg.LastStartTime, + arg.FirstStartTime, + arg.CountryCode, + ) + var count int64 + err := row.Scan(&count) + return count, err +} + +const GetTotalEvents = `-- name: GetTotalEvents :one +SELECT COUNT(*) +FROM event_with_country +WHERE is_live = false + AND status = 'upcoming' + AND ( + league_id = $1 + OR $1 IS NULL + ) + AND ( + sport_id = $2 + OR $2 IS NULL + ) + AND ( + match_name ILIKE '%' || $3 || '%' + OR league_name ILIKE '%' || $3 || '%' + OR $3 IS NULL + ) + AND ( + start_time < $4 + OR $4 IS NULL + ) + AND ( + start_time > $5 + OR $5 IS NULL + ) + AND ( + league_cc = $6 + OR $6 IS NULL + ) +` + +type GetTotalEventsParams struct { + LeagueID pgtype.Int8 `json:"league_id"` + SportID pgtype.Int4 `json:"sport_id"` + Query pgtype.Text `json:"query"` + LastStartTime pgtype.Timestamp `json:"last_start_time"` + FirstStartTime pgtype.Timestamp `json:"first_start_time"` + CountryCode pgtype.Text `json:"country_code"` +} + +func (q *Queries) GetTotalEvents(ctx context.Context, arg GetTotalEventsParams) (int64, error) { + row := q.db.QueryRow(ctx, GetTotalEvents, + arg.LeagueID, + arg.SportID, + arg.Query, + arg.LastStartTime, + arg.FirstStartTime, + arg.CountryCode, + ) + var count int64 + err := row.Scan(&count) + return count, err +} + +const GetUpcomingByID = `-- name: GetUpcomingByID :one +SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc +FROM event_with_country +WHERE id = $1 + AND is_live = false + AND status = 'upcoming' +LIMIT 1 +` + +func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (EventWithCountry, error) { + row := q.db.QueryRow(ctx, GetUpcomingByID, id) + var i EventWithCountry + err := row.Scan( + &i.ID, + &i.SportID, + &i.MatchName, + &i.HomeTeam, + &i.AwayTeam, + &i.HomeTeamID, + &i.AwayTeamID, + &i.HomeKitImage, + &i.AwayKitImage, + &i.LeagueID, + &i.LeagueName, + &i.StartTime, + &i.Score, + &i.MatchMinute, + &i.TimerStatus, + &i.AddedTime, + &i.MatchPeriod, + &i.IsLive, + &i.Status, + &i.FetchedAt, + &i.Source, + &i.DefaultIsActive, + &i.DefaultIsFeatured, + &i.DefaultWinningUpperLimit, + &i.IsMonitored, + &i.LeagueCc, ) return i, err } @@ -427,13 +570,7 @@ INSERT INTO events ( away_kit_image, league_id, league_name, - league_cc, start_time, - score, - match_minute, - timer_status, - added_time, - match_period, is_live, status, source @@ -453,13 +590,7 @@ VALUES ( $12, $13, $14, - $15, - $16, - $17, - $18, - $19, - $20, - $21 + $15 ) ON CONFLICT (id) DO UPDATE SET sport_id = EXCLUDED.sport_id, @@ -480,33 +611,26 @@ SET sport_id = EXCLUDED.sport_id, added_time = EXCLUDED.added_time, match_period = EXCLUDED.match_period, is_live = EXCLUDED.is_live, - status = EXCLUDED.status, source = EXCLUDED.source, fetched_at = now() ` type InsertEventParams struct { ID string `json:"id"` - SportID pgtype.Int4 `json:"sport_id"` - MatchName pgtype.Text `json:"match_name"` - HomeTeam pgtype.Text `json:"home_team"` - AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Int4 `json:"home_team_id"` - AwayTeamID pgtype.Int4 `json:"away_team_id"` - HomeKitImage pgtype.Text `json:"home_kit_image"` - AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Int4 `json:"league_id"` - LeagueName pgtype.Text `json:"league_name"` - LeagueCc pgtype.Text `json:"league_cc"` + 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 pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - Source pgtype.Text `json:"source"` + IsLive bool `json:"is_live"` + Status string `json:"status"` + Source string `json:"source"` } func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error { @@ -522,13 +646,7 @@ func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error arg.AwayKitImage, arg.LeagueID, arg.LeagueName, - arg.LeagueCc, arg.StartTime, - arg.Score, - arg.MatchMinute, - arg.TimerStatus, - arg.AddedTime, - arg.MatchPeriod, arg.IsLive, arg.Status, arg.Source, @@ -536,95 +654,36 @@ func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error return err } -const InsertUpcomingEvent = `-- name: InsertUpcomingEvent :exec -INSERT INTO events ( - id, - sport_id, - match_name, - home_team, - away_team, - home_team_id, - away_team_id, - home_kit_image, - away_kit_image, - league_id, - league_name, - league_cc, - start_time, - is_live, - status, - source +const InsertEventSettings = `-- name: InsertEventSettings :exec +INSERT INTO company_event_settings ( + company_id, + event_id, + is_active, + is_featured, + winning_upper_limit ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - false, - 'upcoming', - $14 - ) ON CONFLICT (id) DO +VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO UPDATE -SET sport_id = EXCLUDED.sport_id, - match_name = EXCLUDED.match_name, - home_team = EXCLUDED.home_team, - 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() +SET is_active = EXCLUDED.is_active, + is_featured = EXCLUDED.is_featured, + winning_upper_limit = EXCLUDED.winning_upper_limit ` -type InsertUpcomingEventParams struct { - ID string `json:"id"` - SportID pgtype.Int4 `json:"sport_id"` - MatchName pgtype.Text `json:"match_name"` - HomeTeam pgtype.Text `json:"home_team"` - AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Int4 `json:"home_team_id"` - AwayTeamID pgtype.Int4 `json:"away_team_id"` - HomeKitImage pgtype.Text `json:"home_kit_image"` - AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Int4 `json:"league_id"` - LeagueName pgtype.Text `json:"league_name"` - LeagueCc pgtype.Text `json:"league_cc"` - StartTime pgtype.Timestamp `json:"start_time"` - Source pgtype.Text `json:"source"` +type InsertEventSettingsParams struct { + CompanyID int64 `json:"company_id"` + EventID string `json:"event_id"` + IsActive pgtype.Bool `json:"is_active"` + IsFeatured pgtype.Bool `json:"is_featured"` + WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"` } -func (q *Queries) InsertUpcomingEvent(ctx context.Context, arg InsertUpcomingEventParams) error { - _, err := q.db.Exec(ctx, InsertUpcomingEvent, - arg.ID, - arg.SportID, - arg.MatchName, - arg.HomeTeam, - arg.AwayTeam, - arg.HomeTeamID, - arg.AwayTeamID, - arg.HomeKitImage, - arg.AwayKitImage, - arg.LeagueID, - arg.LeagueName, - arg.LeagueCc, - arg.StartTime, - arg.Source, +func (q *Queries) InsertEventSettings(ctx context.Context, arg InsertEventSettingsParams) error { + _, err := q.db.Exec(ctx, InsertEventSettings, + arg.CompanyID, + arg.EventID, + arg.IsActive, + arg.IsFeatured, + arg.WinningUpperLimit, ) return err } @@ -644,7 +703,7 @@ func (q *Queries) IsEventMonitored(ctx context.Context, id string) (bool, error) const ListLiveEvents = `-- name: ListLiveEvents :many SELECT id -FROM events +FROM event_with_country WHERE is_live = true ` @@ -668,22 +727,6 @@ func (q *Queries) ListLiveEvents(ctx context.Context) ([]string, error) { return items, nil } -const UpdateEventFeatured = `-- name: UpdateEventFeatured :exec -UPDATE events -SET is_featured = $1 -WHERE id = $2 -` - -type UpdateEventFeaturedParams struct { - IsFeatured bool `json:"is_featured"` - ID string `json:"id"` -} - -func (q *Queries) UpdateEventFeatured(ctx context.Context, arg UpdateEventFeaturedParams) error { - _, err := q.db.Exec(ctx, UpdateEventFeatured, arg.IsFeatured, arg.ID) - return err -} - const UpdateEventMonitored = `-- name: UpdateEventMonitored :exec UPDATE events SET is_monitored = $1 @@ -700,6 +743,40 @@ func (q *Queries) UpdateEventMonitored(ctx context.Context, arg UpdateEventMonit return err } +const UpdateEventSettings = `-- name: UpdateEventSettings :exec +UPDATE company_event_settings +SET is_active = COALESCE($3, is_active), + is_featured = COALESCE( + $4, + is_featured + ), + winning_upper_limit = COALESCE( + $5, + winning_upper_limit + ) +WHERE event_id = $1 + AND company_id = $2 +` + +type UpdateEventSettingsParams struct { + EventID string `json:"event_id"` + CompanyID int64 `json:"company_id"` + IsActive pgtype.Bool `json:"is_active"` + IsFeatured pgtype.Bool `json:"is_featured"` + WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"` +} + +func (q *Queries) UpdateEventSettings(ctx context.Context, arg UpdateEventSettingsParams) error { + _, err := q.db.Exec(ctx, UpdateEventSettings, + arg.EventID, + arg.CompanyID, + arg.IsActive, + arg.IsFeatured, + arg.WinningUpperLimit, + ) + return err +} + const UpdateMatchResult = `-- name: UpdateMatchResult :exec UPDATE events SET score = $1, @@ -709,7 +786,7 @@ WHERE id = $3 type UpdateMatchResultParams struct { Score pgtype.Text `json:"score"` - Status pgtype.Text `json:"status"` + Status string `json:"status"` ID string `json:"id"` } diff --git a/gen/db/events_stat.sql.go b/gen/db/events_stat.sql.go index 2380d54..677fa2a 100644 --- a/gen/db/events_stat.sql.go +++ b/gen/db/events_stat.sql.go @@ -56,10 +56,6 @@ SELECT leagues.id, ) AS removed FROM leagues JOIN events ON leagues.id = events.league_id -WHERE ( - leagues.is_featured = $1 - OR $1 IS NULL - ) GROUP BY leagues.id, leagues.name ` @@ -83,8 +79,8 @@ type GetLeagueEventStatRow struct { Removed int64 `json:"removed"` } -func (q *Queries) GetLeagueEventStat(ctx context.Context, isLeagueFeatured pgtype.Bool) ([]GetLeagueEventStatRow, error) { - rows, err := q.db.Query(ctx, GetLeagueEventStat, isLeagueFeatured) +func (q *Queries) GetLeagueEventStat(ctx context.Context) ([]GetLeagueEventStatRow, error) { + rows, err := q.db.Query(ctx, GetLeagueEventStat) if err != nil { return nil, err } @@ -126,34 +122,20 @@ SELECT DATE_TRUNC('month', start_time) AS month, FROM events JOIN leagues ON leagues.id = events.league_id WHERE ( - events.is_featured = $1 + events.league_id = $1 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 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 { Month pgtype.Interval `json:"month"` EventCount int64 `json:"event_count"` } -func (q *Queries) GetTotalMontlyEventStat(ctx context.Context, arg GetTotalMontlyEventStatParams) ([]GetTotalMontlyEventStatRow, error) { - rows, err := q.db.Query(ctx, GetTotalMontlyEventStat, arg.IsEventFeatured, arg.IsLeagueFeatured, arg.LeagueID) +func (q *Queries) GetTotalMontlyEventStat(ctx context.Context, leagueID pgtype.Int8) ([]GetTotalMontlyEventStatRow, error) { + rows, err := q.db.Query(ctx, GetTotalMontlyEventStat, leagueID) if err != nil { return nil, err } diff --git a/gen/db/flags.sql.go b/gen/db/flags.sql.go index 17b406e..653543f 100644 --- a/gen/db/flags.sql.go +++ b/gen/db/flags.sql.go @@ -14,26 +14,26 @@ import ( const CreateFlag = `-- name: CreateFlag :one INSERT INTO flags ( bet_id, - odd_id, + odds_market_id, reason ) VALUES ( $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 { - BetID pgtype.Int8 `json:"bet_id"` - OddID pgtype.Int8 `json:"odd_id"` - Reason pgtype.Text `json:"reason"` + BetID pgtype.Int8 `json:"bet_id"` + OddsMarketID pgtype.Int8 `json:"odds_market_id"` + Reason pgtype.Text `json:"reason"` } 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 err := row.Scan( &i.ID, &i.BetID, - &i.OddID, + &i.OddsMarketID, &i.Reason, &i.FlaggedAt, &i.Resolved, diff --git a/gen/db/leagues.sql.go b/gen/db/leagues.sql.go index 143f6ca..5d49d4d 100644 --- a/gen/db/leagues.sql.go +++ b/gen/db/leagues.sql.go @@ -14,27 +14,27 @@ import ( const CheckLeagueSupport = `-- name: CheckLeagueSupport :one SELECT EXISTS( SELECT 1 - FROM leagues - WHERE id = $1 + FROM company_league_settings + WHERE league_id = $1 + AND company_id = $2 AND is_active = true ) ` -func (q *Queries) CheckLeagueSupport(ctx context.Context, id int64) (bool, error) { - row := q.db.QueryRow(ctx, CheckLeagueSupport, id) +type CheckLeagueSupportParams struct { + 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 err := row.Scan(&exists) return exists, err } const GetAllLeagues = `-- name: GetAllLeagues :many -SELECT id, - name, - country_code, - bet365_id, - is_active, - is_featured, - sport_id +SELECT id, name, img_url, country_code, bet365_id, sport_id, default_is_active, default_is_featured FROM leagues WHERE ( country_code = $1 @@ -44,44 +44,21 @@ WHERE ( sport_id = $2 OR $2 IS NULL ) - AND ( - is_active = $3 - OR $3 IS NULL - ) - AND ( - is_featured = $4 - OR $4 IS NULL - ) -ORDER BY is_featured DESC, - name ASC -LIMIT $6 OFFSET $5 +ORDER BY name ASC +LIMIT $4 OFFSET $3 ` type GetAllLeaguesParams struct { CountryCode pgtype.Text `json:"country_code"` SportID pgtype.Int4 `json:"sport_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` Offset pgtype.Int4 `json:"offset"` Limit pgtype.Int4 `json:"limit"` } -type GetAllLeaguesRow struct { - 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) { +func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([]League, error) { rows, err := q.db.Query(ctx, GetAllLeagues, arg.CountryCode, arg.SportID, - arg.IsActive, - arg.IsFeatured, arg.Offset, arg.Limit, ) @@ -89,17 +66,18 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([ return nil, err } defer rows.Close() - var items []GetAllLeaguesRow + var items []League for rows.Next() { - var i GetAllLeaguesRow + var i League if err := rows.Scan( &i.ID, &i.Name, + &i.ImgUrl, &i.CountryCode, &i.Bet365ID, - &i.IsActive, - &i.IsFeatured, &i.SportID, + &i.DefaultIsActive, + &i.DefaultIsFeatured, ); err != nil { return nil, err } @@ -111,45 +89,71 @@ func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([ return items, nil } -const GetFeaturedLeagues = `-- name: GetFeaturedLeagues :many -SELECT id, - name, - country_code, - bet365_id, - is_active, - is_featured, - sport_id -FROM leagues -WHERE is_featured = true +const GetAllLeaguesWithSettings = `-- name: GetAllLeaguesWithSettings :many +SELECT id, name, img_url, country_code, bet365_id, sport_id, default_is_active, default_is_featured, company_id, is_active, is_featured, updated_at +FROM league_with_settings +WHERE (company_id = $1) + AND ( + country_code = $2 + OR $2 IS NULL + ) + AND ( + 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 { - ID int64 `json:"id"` - Name string `json:"name"` +type GetAllLeaguesWithSettingsParams struct { + CompanyID int64 `json:"company_id"` CountryCode pgtype.Text `json:"country_code"` - Bet365ID pgtype.Int4 `json:"bet365_id"` + SportID pgtype.Int4 `json:"sport_id"` IsActive pgtype.Bool `json:"is_active"` 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) { - rows, err := q.db.Query(ctx, GetFeaturedLeagues) +func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeaguesWithSettingsParams) ([]LeagueWithSetting, error) { + rows, err := q.db.Query(ctx, GetAllLeaguesWithSettings, + arg.CompanyID, + arg.CountryCode, + arg.SportID, + arg.IsActive, + arg.IsFeatured, + arg.Offset, + arg.Limit, + ) if err != nil { return nil, err } defer rows.Close() - var items []GetFeaturedLeaguesRow + var items []LeagueWithSetting for rows.Next() { - var i GetFeaturedLeaguesRow + var i LeagueWithSetting if err := rows.Scan( &i.ID, &i.Name, + &i.ImgUrl, &i.CountryCode, &i.Bet365ID, + &i.SportID, + &i.DefaultIsActive, + &i.DefaultIsFeatured, + &i.CompanyID, &i.IsActive, &i.IsFeatured, - &i.SportID, + &i.UpdatedAt, ); err != nil { return nil, err } @@ -168,27 +172,25 @@ INSERT INTO leagues ( country_code, bet365_id, sport_id, - is_active, - is_featured + default_is_active, + default_is_featured ) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, country_code = EXCLUDED.country_code, bet365_id = EXCLUDED.bet365_id, - is_active = EXCLUDED.is_active, - is_featured = EXCLUDED.is_featured, sport_id = EXCLUDED.sport_id ` type InsertLeagueParams struct { - ID int64 `json:"id"` - Name string `json:"name"` - CountryCode pgtype.Text `json:"country_code"` - Bet365ID pgtype.Int4 `json:"bet365_id"` - SportID int32 `json:"sport_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` + ID int64 `json:"id"` + Name string `json:"name"` + 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"` } 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.Bet365ID, arg.SportID, - arg.IsActive, - arg.IsFeatured, + arg.DefaultIsActive, + arg.DefaultIsFeatured, ) return err } -const SetLeagueActive = `-- name: SetLeagueActive :exec -UPDATE leagues -SET is_active = $2 -WHERE id = $1 +const InsertLeagueSettings = `-- name: InsertLeagueSettings :exec +INSERT INTO company_league_settings ( + company_id, + 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 { - ID int64 `json:"id"` - IsActive pgtype.Bool `json:"is_active"` +type InsertLeagueSettingsParams struct { + CompanyID int64 `json:"company_id"` + LeagueID int64 `json:"league_id"` + IsActive pgtype.Bool `json:"is_active"` + IsFeatured pgtype.Bool `json:"is_featured"` } -func (q *Queries) SetLeagueActive(ctx context.Context, arg SetLeagueActiveParams) error { - _, err := q.db.Exec(ctx, SetLeagueActive, arg.ID, arg.IsActive) +func (q *Queries) InsertLeagueSettings(ctx context.Context, arg InsertLeagueSettingsParams) error { + _, err := q.db.Exec(ctx, InsertLeagueSettings, + arg.CompanyID, + arg.LeagueID, + arg.IsActive, + arg.IsFeatured, + ) return err } @@ -225,9 +241,7 @@ UPDATE leagues SET name = COALESCE($2, name), country_code = COALESCE($3, country_code), bet365_id = COALESCE($4, bet365_id), - is_active = COALESCE($5, is_active), - is_featured = COALESCE($6, is_featured), - sport_id = COALESCE($7, sport_id) + sport_id = COALESCE($5, sport_id) WHERE id = $1 ` @@ -236,8 +250,6 @@ type UpdateLeagueParams struct { Name pgtype.Text `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 pgtype.Int4 `json:"sport_id"` } @@ -247,43 +259,35 @@ func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) erro arg.Name, arg.CountryCode, arg.Bet365ID, - arg.IsActive, - arg.IsFeatured, arg.SportID, ) return err } -const UpdateLeagueByBet365ID = `-- name: UpdateLeagueByBet365ID :exec -UPDATE leagues -SET name = COALESCE($2, name), - id = COALESCE($3, id), - country_code = COALESCE($4, country_code), - is_active = COALESCE($5, is_active), - is_featured = COALESCE($6, is_featured), - sport_id = COALESCE($7, sport_id) -WHERE bet365_id = $1 +const UpdateLeagueSettings = `-- name: UpdateLeagueSettings :exec +UPDATE company_league_settings +SET is_active = COALESCE($3, is_active), + is_featured = COALESCE( + $4, + is_featured + ) +WHERE league_id = $1 + AND company_id = $2 ` -type UpdateLeagueByBet365IDParams struct { - Bet365ID pgtype.Int4 `json:"bet365_id"` - Name pgtype.Text `json:"name"` - ID pgtype.Int8 `json:"id"` - CountryCode pgtype.Text `json:"country_code"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` - SportID pgtype.Int4 `json:"sport_id"` +type UpdateLeagueSettingsParams struct { + LeagueID int64 `json:"league_id"` + CompanyID int64 `json:"company_id"` + IsActive pgtype.Bool `json:"is_active"` + IsFeatured pgtype.Bool `json:"is_featured"` } -func (q *Queries) UpdateLeagueByBet365ID(ctx context.Context, arg UpdateLeagueByBet365IDParams) error { - _, err := q.db.Exec(ctx, UpdateLeagueByBet365ID, - arg.Bet365ID, - arg.Name, - arg.ID, - arg.CountryCode, +func (q *Queries) UpdateLeagueSettings(ctx context.Context, arg UpdateLeagueSettingsParams) error { + _, err := q.db.Exec(ctx, UpdateLeagueSettings, + arg.LeagueID, + arg.CompanyID, arg.IsActive, arg.IsFeatured, - arg.SportID, ) return err } diff --git a/gen/db/models.go b/gen/db/models.go index 9b1a3fa..460b84d 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -75,6 +75,7 @@ type Bank struct { type Bet struct { ID int64 `json:"id"` + CompanyID int64 `json:"company_id"` Amount int64 `json:"amount"` TotalOdds float32 `json:"total_odds"` Status int32 `json:"status"` @@ -108,6 +109,7 @@ type BetOutcome struct { type BetWithOutcome struct { ID int64 `json:"id"` + CompanyID int64 `json:"company_id"` Amount int64 `json:"amount"` TotalOdds float32 `json:"total_odds"` Status int32 `json:"status"` @@ -125,8 +127,8 @@ type BetWithOutcome struct { } type Bonu struct { - ID int64 `json:"id"` Multiplier float32 `json:"multiplier"` + ID int64 `json:"id"` BalanceCap int64 `json:"balance_cap"` } @@ -184,6 +186,7 @@ type BranchOperation struct { type CompaniesDetail struct { ID int64 `json:"id"` Name string `json:"name"` + Slug string `json:"slug"` AdminID int64 `json:"admin_id"` WalletID int64 `json:"wallet_id"` DeductedPercentage float32 `json:"deducted_percentage"` @@ -200,6 +203,7 @@ type CompaniesDetail struct { type Company struct { ID int64 `json:"id"` Name string `json:"name"` + Slug string `json:"slug"` AdminID int64 `json:"admin_id"` WalletID int64 `json:"wallet_id"` DeductedPercentage float32 `json:"deducted_percentage"` @@ -208,13 +212,32 @@ type Company struct { UpdatedAt pgtype.Timestamp `json:"updated_at"` } -type CustomOdd struct { - ID int64 `json:"id"` - OddID int64 `json:"odd_id"` - RawOddID int64 `json:"raw_odd_id"` - EventID string `json:"event_id"` - OddValue float64 `json:"odd_value"` - CreatedAt pgtype.Timestamp `json:"created_at"` +type CompanyEventSetting struct { + ID int64 `json:"id"` + CompanyID int64 `json:"company_id"` + EventID string `json:"event_id"` + IsActive pgtype.Bool `json:"is_active"` + IsFeatured pgtype.Bool `json:"is_featured"` + WinningUpperLimit pgtype.Int4 `json:"winning_upper_limit"` + 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 { @@ -258,40 +281,40 @@ type DirectDeposit struct { } type DisabledOdd struct { - ID int64 `json:"id"` - OddID int64 `json:"odd_id"` - RawOddID int64 `json:"raw_odd_id"` - EventID string `json:"event_id"` - CreatedAt pgtype.Timestamp `json:"created_at"` + ID int64 `json:"id"` + CompanyID int64 `json:"company_id"` + OddsMarketID int64 `json:"odds_market_id"` + RawOddID int64 `json:"raw_odd_id"` + EventID string `json:"event_id"` + CreatedAt pgtype.Timestamp `json:"created_at"` } type Event struct { - ID string `json:"id"` - SportID pgtype.Int4 `json:"sport_id"` - MatchName pgtype.Text `json:"match_name"` - HomeTeam pgtype.Text `json:"home_team"` - AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Int4 `json:"home_team_id"` - AwayTeamID pgtype.Int4 `json:"away_team_id"` - HomeKitImage pgtype.Text `json:"home_kit_image"` - AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Int4 `json:"league_id"` - LeagueName pgtype.Text `json:"league_name"` - LeagueCc pgtype.Text `json:"league_cc"` - 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 pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - Source pgtype.Text `json:"source"` - IsFeatured bool `json:"is_featured"` - IsMonitored bool `json:"is_monitored"` - WinningUpperLimit int32 `json:"winning_upper_limit"` - IsActive bool `json:"is_active"` + 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"` } type EventHistory struct { @@ -301,6 +324,69 @@ type EventHistory struct { 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 { ID int32 `json:"id"` FromCurrency string `json:"from_currency"` @@ -318,23 +404,38 @@ type FavoriteGame struct { } type Flag struct { - ID int64 `json:"id"` - BetID pgtype.Int8 `json:"bet_id"` - OddID pgtype.Int8 `json:"odd_id"` - Reason pgtype.Text `json:"reason"` - FlaggedAt pgtype.Timestamp `json:"flagged_at"` - Resolved pgtype.Bool `json:"resolved"` + ID int64 `json:"id"` + BetID pgtype.Int8 `json:"bet_id"` + OddsMarketID pgtype.Int8 `json:"odds_market_id"` + Reason pgtype.Text `json:"reason"` + FlaggedAt pgtype.Timestamp `json:"flagged_at"` + Resolved pgtype.Bool `json:"resolved"` } type League struct { - ID int64 `json:"id"` - Name string `json:"name"` - Img pgtype.Text `json:"img"` - CountryCode pgtype.Text `json:"country_code"` - Bet365ID pgtype.Int4 `json:"bet365_id"` - SportID int32 `json:"sport_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` + 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"` +} + +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 { @@ -354,34 +455,61 @@ type Notification struct { 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 { + ID int64 `json:"id"` + OddsMarketID int64 `json:"odds_market_id"` + RawOddID int64 `json:"raw_odd_id"` + MarketID string `json:"market_id"` + EventID string `json:"event_id"` + OddValue float64 `json:"odd_value"` + CreatedAt pgtype.Timestamp `json:"created_at"` } -type OddHistory struct { - ID int64 `json:"id"` - OddID int64 `json:"odd_id"` - RawOddID int64 `json:"raw_odd_id"` - MarketID string `json:"market_id"` - EventID string `json:"event_id"` - OddValue float64 `json:"odd_value"` - 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 { @@ -599,15 +727,16 @@ type SupportedOperation struct { } type Team struct { - ID string `json:"id"` - TeamName string `json:"team_name"` - Country pgtype.Text `json:"country"` - Bet365ID pgtype.Int4 `json:"bet365_id"` - LogoUrl pgtype.Text `json:"logo_url"` + ID int64 `json:"id"` + TeamName string `json:"team_name"` + CountryCode string `json:"country_code"` + Bet365ID pgtype.Int8 `json:"bet365_id"` + ImgUrl pgtype.Text `json:"img_url"` } type Ticket struct { ID int64 `json:"id"` + CompanyID int64 `json:"company_id"` Amount int64 `json:"amount"` TotalOdds float32 `json:"total_odds"` Ip string `json:"ip"` @@ -634,6 +763,7 @@ type TicketOutcome struct { type TicketWithOutcome struct { ID int64 `json:"id"` + CompanyID int64 `json:"company_id"` Amount int64 `json:"amount"` TotalOdds float32 `json:"total_odds"` Ip string `json:"ip"` @@ -738,6 +868,7 @@ type VirtualGameTransaction struct { type Wallet struct { ID int64 `json:"id"` Balance int64 `json:"balance"` + Currency string `json:"currency"` IsWithdraw bool `json:"is_withdraw"` IsBettable bool `json:"is_bettable"` IsTransferable bool `json:"is_transferable"` @@ -746,7 +877,6 @@ type Wallet struct { IsActive bool `json:"is_active"` CreatedAt pgtype.Timestamp `json:"created_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"` - Currency string `json:"currency"` BonusBalance pgtype.Numeric `json:"bonus_balance"` CashBalance pgtype.Numeric `json:"cash_balance"` } diff --git a/gen/db/odd_history.sql.go b/gen/db/odd_history.sql.go index fa5272a..0a0333d 100644 --- a/gen/db/odd_history.sql.go +++ b/gen/db/odd_history.sql.go @@ -12,10 +12,10 @@ import ( ) 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 WHERE ( - odd_id = $1 + odds_market_id = $1 OR $1 IS NULL ) AND ( @@ -67,7 +67,7 @@ func (q *Queries) GetAllOddHistory(ctx context.Context, arg GetAllOddHistoryPara var i OddHistory if err := rows.Scan( &i.ID, - &i.OddID, + &i.OddsMarketID, &i.RawOddID, &i.MarketID, &i.EventID, @@ -85,10 +85,10 @@ func (q *Queries) GetAllOddHistory(ctx context.Context, arg GetAllOddHistoryPara } 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 WHERE ( - odd_id = $2 + odds_market_id = $2 OR $2 IS NULL ) AND ( @@ -144,7 +144,7 @@ func (q *Queries) GetInitialOddPerDay(ctx context.Context, arg GetInitialOddPerD var i OddHistory if err := rows.Scan( &i.ID, - &i.OddID, + &i.OddsMarketID, &i.RawOddID, &i.MarketID, &i.EventID, @@ -163,27 +163,27 @@ func (q *Queries) GetInitialOddPerDay(ctx context.Context, arg GetInitialOddPerD const InsertOddHistory = `-- name: InsertOddHistory :one INSERT INTO odd_history ( - odd_id, + odds_market_id, market_id, raw_odd_id, event_id, odd_value ) 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 { - OddID int64 `json:"odd_id"` - MarketID string `json:"market_id"` - RawOddID int64 `json:"raw_odd_id"` - EventID string `json:"event_id"` - OddValue float64 `json:"odd_value"` + OddsMarketID int64 `json:"odds_market_id"` + MarketID string `json:"market_id"` + RawOddID int64 `json:"raw_odd_id"` + EventID string `json:"event_id"` + OddValue float64 `json:"odd_value"` } func (q *Queries) InsertOddHistory(ctx context.Context, arg InsertOddHistoryParams) (OddHistory, error) { row := q.db.QueryRow(ctx, InsertOddHistory, - arg.OddID, + arg.OddsMarketID, arg.MarketID, arg.RawOddID, arg.EventID, @@ -192,7 +192,7 @@ func (q *Queries) InsertOddHistory(ctx context.Context, arg InsertOddHistoryPara var i OddHistory err := row.Scan( &i.ID, - &i.OddID, + &i.OddsMarketID, &i.RawOddID, &i.MarketID, &i.EventID, diff --git a/gen/db/odds.sql.go b/gen/db/odds.sql.go index 2865054..966fcff 100644 --- a/gen/db/odds.sql.go +++ b/gen/db/odds.sql.go @@ -12,49 +12,168 @@ import ( ) const DeleteOddsForEvent = `-- name: DeleteOddsForEvent :exec -DELETE FROM odds -Where fi = $1 +DELETE FROM odds_market +Where event_id = $1 ` -func (q *Queries) DeleteOddsForEvent(ctx context.Context, fi pgtype.Text) error { - _, err := q.db.Exec(ctx, DeleteOddsForEvent, fi) +func (q *Queries) DeleteOddsForEvent(ctx context.Context, eventID string) error { + _, err := q.db.Exec(ctx, DeleteOddsForEvent, eventID) return err } -const GetALLPrematchOdds = `-- name: GetALLPrematchOdds :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 -FROM odds -WHERE is_active = true - AND source = 'bet365' +const GetAllOdds = `-- name: GetAllOdds :many +SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source +FROM odds_market_with_event +LIMIT $2 OFFSET $1 ` -func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]Odd, error) { - rows, err := q.db.Query(ctx, GetALLPrematchOdds) +type GetAllOddsParams struct { + 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 { return nil, err } defer rows.Close() - var items []Odd + var items []OddsMarketWithEvent for rows.Next() { - var i Odd + var i OddsMarketWithEvent 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.DefaultIsActive, &i.FetchedAt, &i.ExpiresAt, + &i.IsMonitored, + &i.IsLive, + &i.Status, &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.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 { return nil, err } @@ -67,89 +186,83 @@ func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]Odd, error) { } 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 -FROM odds +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 market_id = $1 - AND fi = $2 - AND is_active = true - AND source = 'bet365' + AND event_id = $2 ` type GetOddsByMarketIDParams struct { - MarketID pgtype.Text `json:"market_id"` - Fi pgtype.Text `json:"fi"` + MarketID string `json:"market_id"` + EventID string `json:"event_id"` } -func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDParams) (Odd, error) { - row := q.db.QueryRow(ctx, GetOddsByMarketID, arg.MarketID, arg.Fi) - var i Odd +func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDParams) (OddsMarketWithEvent, error) { + row := q.db.QueryRow(ctx, GetOddsByMarketID, arg.MarketID, arg.EventID) + var i OddsMarketWithEvent err := row.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.DefaultIsActive, &i.FetchedAt, &i.ExpiresAt, + &i.IsMonitored, + &i.IsLive, + &i.Status, &i.Source, - &i.IsActive, ) return i, err } -const GetPaginatedPrematchOddsByUpcomingID = `-- name: GetPaginatedPrematchOddsByUpcomingID :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 -FROM odds o - JOIN events e ON o.fi = e.id -WHERE e.id = $1 - AND e.is_live = false - AND e.status = 'upcoming' - AND o.is_active = true - AND o.source = 'bet365' -LIMIT $3 OFFSET $2 +const GetOddsWithSettingsByEventID = `-- name: GetOddsWithSettingsByEventID :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 event_id = $1 + AND company_id = $2 +LIMIT $4 OFFSET $3 ` -type GetPaginatedPrematchOddsByUpcomingIDParams struct { - ID string `json:"id"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` +type GetOddsWithSettingsByEventIDParams struct { + EventID string `json:"event_id"` + CompanyID int64 `json:"company_id"` + Offset pgtype.Int4 `json:"offset"` + Limit pgtype.Int4 `json:"limit"` } -func (q *Queries) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, arg GetPaginatedPrematchOddsByUpcomingIDParams) ([]Odd, error) { - rows, err := q.db.Query(ctx, GetPaginatedPrematchOddsByUpcomingID, arg.ID, arg.Offset, arg.Limit) +func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsWithSettingsByEventIDParams) ([]OddsMarketWithSetting, error) { + rows, err := q.db.Query(ctx, GetOddsWithSettingsByEventID, + arg.EventID, + arg.CompanyID, + arg.Offset, + arg.Limit, + ) if err != nil { return nil, err } defer rows.Close() - var items []Odd + var items []OddsMarketWithSetting for rows.Next() { - var i Odd + var i OddsMarketWithSetting 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.DefaultIsActive, &i.FetchedAt, &i.ExpiresAt, - &i.Source, + &i.CompanyID, &i.IsActive, + &i.RawOdds, + &i.UpdatedAt, ); err != nil { return nil, err } @@ -161,116 +274,80 @@ func (q *Queries) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, arg return items, nil } -const GetPrematchOdds = `-- name: GetPrematchOdds :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 -FROM odds -WHERE is_active = true - AND source = 'bet365' +const GetOddsWithSettingsByMarketID = `-- name: GetOddsWithSettingsByMarketID :one +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 market_id = $1 + AND event_id = $2 + AND company_id = $3 ` -func (q *Queries) GetPrematchOdds(ctx context.Context) ([]Odd, error) { - rows, err := q.db.Query(ctx, GetPrematchOdds) - if err != nil { - return nil, err - } - 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 +type GetOddsWithSettingsByMarketIDParams struct { + MarketID string `json:"market_id"` + EventID string `json:"event_id"` + CompanyID int64 `json:"company_id"` } -const GetPrematchOddsByUpcomingID = `-- name: GetPrematchOddsByUpcomingID :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 -FROM odds o - JOIN events e ON o.fi = e.id -WHERE e.id = $1 - AND e.is_live = false - AND e.status = 'upcoming' - AND o.is_active = true - AND o.source = 'bet365' +func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (OddsMarketWithSetting, error) { + row := q.db.QueryRow(ctx, GetOddsWithSettingsByMarketID, arg.MarketID, arg.EventID, arg.CompanyID) + var i OddsMarketWithSetting + err := row.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.RawOdds, + &i.UpdatedAt, + ) + return i, err +} + +const InsertOddSettings = `-- name: InsertOddSettings :exec +INSERT INTO company_odd_settings ( + company_id, + odds_market_id, + is_active, + custom_raw_odds + ) +VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO +UPDATE +SET is_active = EXCLUDED.is_active, + custom_raw_odds = EXCLUDED.custom_raw_odds ` -func (q *Queries) GetPrematchOddsByUpcomingID(ctx context.Context, id string) ([]Odd, error) { - rows, err := q.db.Query(ctx, GetPrematchOddsByUpcomingID, id) - if err != nil { - return nil, err - } - 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 +type InsertOddSettingsParams struct { + CompanyID int64 `json:"company_id"` + OddsMarketID int64 `json:"odds_market_id"` + IsActive pgtype.Bool `json:"is_active"` + CustomRawOdds []byte `json:"custom_raw_odds"` } -const InsertNonLiveOdd = `-- name: InsertNonLiveOdd :exec -INSERT INTO odds ( +func (q *Queries) InsertOddSettings(ctx context.Context, arg InsertOddSettingsParams) error { + _, err := q.db.Exec(ctx, InsertOddSettings, + arg.CompanyID, + arg.OddsMarketID, + arg.IsActive, + arg.CustomRawOdds, + ) + return err +} + +const InsertOddsMarket = `-- name: InsertOddsMarket :exec +INSERT INTO odds_market ( event_id, - fi, market_type, market_name, market_category, market_id, - name, - handicap, - odds_value, - section, - category, raw_odds, - is_active, - source, fetched_at, expires_at ) @@ -282,65 +359,36 @@ VALUES ( $5, $6, $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15, - $16 + $8 ) ON CONFLICT (event_id, market_id) DO UPDATE -SET odds_value = EXCLUDED.odds_value, - raw_odds = EXCLUDED.raw_odds, - market_type = EXCLUDED.market_type, +SET market_type = EXCLUDED.market_type, market_name = EXCLUDED.market_name, market_category = EXCLUDED.market_category, - name = EXCLUDED.name, - handicap = EXCLUDED.handicap, + raw_odds = EXCLUDED.raw_odds, fetched_at = EXCLUDED.fetched_at, - is_active = EXCLUDED.is_active, - source = EXCLUDED.source, - fi = EXCLUDED.fi + expires_at = EXCLUDED.expires_at ` -type InsertNonLiveOddParams struct { - EventID pgtype.Text `json:"event_id"` - Fi pgtype.Text `json:"fi"` +type InsertOddsMarketParams struct { + EventID string `json:"event_id"` 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"` + MarketName string `json:"market_name"` + MarketCategory string `json:"market_category"` + MarketID string `json:"market_id"` RawOdds []byte `json:"raw_odds"` - IsActive pgtype.Bool `json:"is_active"` - Source pgtype.Text `json:"source"` FetchedAt pgtype.Timestamp `json:"fetched_at"` ExpiresAt pgtype.Timestamp `json:"expires_at"` } -func (q *Queries) InsertNonLiveOdd(ctx context.Context, arg InsertNonLiveOddParams) error { - _, err := q.db.Exec(ctx, InsertNonLiveOdd, +func (q *Queries) InsertOddsMarket(ctx context.Context, arg InsertOddsMarketParams) error { + _, err := q.db.Exec(ctx, InsertOddsMarket, arg.EventID, - arg.Fi, arg.MarketType, arg.MarketName, arg.MarketCategory, arg.MarketID, - arg.Name, - arg.Handicap, - arg.OddsValue, - arg.Section, - arg.Category, arg.RawOdds, - arg.IsActive, - arg.Source, arg.FetchedAt, arg.ExpiresAt, ) diff --git a/gen/db/ticket.sql.go b/gen/db/ticket.sql.go index c72c1ea..90e11f6 100644 --- a/gen/db/ticket.sql.go +++ b/gen/db/ticket.sql.go @@ -27,7 +27,7 @@ func (q *Queries) CountTicketByIP(ctx context.Context, ip string) (int64, error) const CreateTicket = `-- name: CreateTicket :one INSERT INTO tickets (amount, total_odds, ip) 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 { @@ -41,6 +41,7 @@ func (q *Queries) CreateTicket(ctx context.Context, arg CreateTicketParams) (Tic var i Ticket err := row.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Ip, @@ -96,7 +97,7 @@ func (q *Queries) DeleteTicketOutcome(ctx context.Context, ticketID int64) error } 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 ` @@ -111,6 +112,7 @@ func (q *Queries) GetAllTickets(ctx context.Context) ([]TicketWithOutcome, error var i TicketWithOutcome if err := rows.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Ip, @@ -152,7 +154,7 @@ func (q *Queries) GetAllTicketsInRange(ctx context.Context, arg GetAllTicketsInR } 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 WHERE id = $1 ` @@ -162,6 +164,7 @@ func (q *Queries) GetTicketByID(ctx context.Context, id int64) (TicketWithOutcom var i TicketWithOutcome err := row.Scan( &i.ID, + &i.CompanyID, &i.Amount, &i.TotalOdds, &i.Ip, diff --git a/gen/db/user.sql.go b/gen/db/user.sql.go index 48faa10..0d4c33b 100644 --- a/gen/db/user.sql.go +++ b/gen/db/user.sql.go @@ -17,17 +17,20 @@ SELECT EXISTS ( FROM users WHERE users.phone_number = $1 AND users.phone_number IS NOT NULL + AND users.company_id = $2 ) AS phone_exists, EXISTS ( SELECT 1 FROM users - WHERE users.email = $2 + WHERE users.email = $3 AND users.email IS NOT NULL + AND users.company_id = $2 ) AS email_exists ` type CheckPhoneEmailExistParams struct { PhoneNumber pgtype.Text `json:"phone_number"` + CompanyID pgtype.Int8 `json:"company_id"` Email pgtype.Text `json:"email"` } @@ -37,7 +40,7 @@ type CheckPhoneEmailExistRow struct { } 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 err := row.Scan(&i.PhoneExists, &i.EmailExists) return i, err @@ -339,8 +342,14 @@ SELECT id, company_id FROM users WHERE email = $1 + AND company_id = $2 ` +type GetUserByEmailParams struct { + Email pgtype.Text `json:"email"` + CompanyID pgtype.Int8 `json:"company_id"` +} + type GetUserByEmailRow struct { ID int64 `json:"id"` FirstName string `json:"first_name"` @@ -357,8 +366,8 @@ type GetUserByEmailRow struct { CompanyID pgtype.Int8 `json:"company_id"` } -func (q *Queries) GetUserByEmail(ctx context.Context, email pgtype.Text) (GetUserByEmailRow, error) { - row := q.db.QueryRow(ctx, GetUserByEmail, email) +func (q *Queries) GetUserByEmail(ctx context.Context, arg GetUserByEmailParams) (GetUserByEmailRow, error) { + row := q.db.QueryRow(ctx, GetUserByEmail, arg.Email, arg.CompanyID) var i GetUserByEmailRow err := row.Scan( &i.ID, @@ -424,8 +433,14 @@ SELECT id, company_id FROM users 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 { ID int64 `json:"id"` FirstName string `json:"first_name"` @@ -442,8 +457,8 @@ type GetUserByPhoneRow struct { CompanyID pgtype.Int8 `json:"company_id"` } -func (q *Queries) GetUserByPhone(ctx context.Context, phoneNumber pgtype.Text) (GetUserByPhoneRow, error) { - row := q.db.QueryRow(ctx, GetUserByPhone, phoneNumber) +func (q *Queries) GetUserByPhone(ctx context.Context, arg GetUserByPhoneParams) (GetUserByPhoneRow, error) { + row := q.db.QueryRow(ctx, GetUserByPhone, arg.PhoneNumber, arg.CompanyID) var i GetUserByPhoneRow err := row.Scan( &i.ID, @@ -478,25 +493,22 @@ SELECT id, suspended_at, company_id FROM users -WHERE ( - first_name ILIKE '%' || $1 || '%' - OR last_name ILIKE '%' || $1 || '%' - OR phone_number LIKE '%' || $1 || '%' +WHERE (company_id = $1) + AND ( + first_name ILIKE '%' || $2 || '%' + OR last_name ILIKE '%' || $2 || '%' + OR phone_number LIKE '%' || $2 || '%' ) AND ( - role = $2 - OR $2 IS NULL - ) - AND ( - company_id = $3 + role = $3 OR $3 IS NULL ) ` type SearchUserByNameOrPhoneParams struct { - Column1 pgtype.Text `json:"column_1"` - Role pgtype.Text `json:"role"` CompanyID pgtype.Int8 `json:"company_id"` + Column2 pgtype.Text `json:"column_2"` + Role pgtype.Text `json:"role"` } type SearchUserByNameOrPhoneRow struct { @@ -516,7 +528,7 @@ type SearchUserByNameOrPhoneRow struct { } 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 { return nil, err } @@ -575,6 +587,7 @@ SET password = $1, WHERE ( email = $2 OR phone_number = $3 + AND company_id = $4 ) ` diff --git a/gen/db/wallet.sql.go b/gen/db/wallet.sql.go index 1cb7387..7e3eb7f 100644 --- a/gen/db/wallet.sql.go +++ b/gen/db/wallet.sql.go @@ -50,7 +50,7 @@ INSERT INTO wallets ( type ) 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 { @@ -73,6 +73,7 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal err := row.Scan( &i.ID, &i.Balance, + &i.Currency, &i.IsWithdraw, &i.IsBettable, &i.IsTransferable, @@ -81,7 +82,6 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal &i.IsActive, &i.CreatedAt, &i.UpdatedAt, - &i.Currency, &i.BonusBalance, &i.CashBalance, ) @@ -188,7 +188,7 @@ func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDet } 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 ` @@ -204,6 +204,7 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) { if err := rows.Scan( &i.ID, &i.Balance, + &i.Currency, &i.IsWithdraw, &i.IsBettable, &i.IsTransferable, @@ -212,7 +213,6 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) { &i.IsActive, &i.CreatedAt, &i.UpdatedAt, - &i.Currency, &i.BonusBalance, &i.CashBalance, ); err != nil { @@ -319,7 +319,7 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (Cust } 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 WHERE id = $1 ` @@ -330,6 +330,7 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) { err := row.Scan( &i.ID, &i.Balance, + &i.Currency, &i.IsWithdraw, &i.IsBettable, &i.IsTransferable, @@ -338,7 +339,6 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) { &i.IsActive, &i.CreatedAt, &i.UpdatedAt, - &i.Currency, &i.BonusBalance, &i.CashBalance, ) @@ -346,7 +346,7 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) { } 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 WHERE user_id = $1 ` @@ -363,6 +363,7 @@ func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet if err := rows.Scan( &i.ID, &i.Balance, + &i.Currency, &i.IsWithdraw, &i.IsBettable, &i.IsTransferable, @@ -371,7 +372,6 @@ func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet &i.IsActive, &i.CreatedAt, &i.UpdatedAt, - &i.Currency, &i.BonusBalance, &i.CashBalance, ); err != nil { diff --git a/internal/domain/common_log.go b/internal/domain/common_log.go new file mode 100644 index 0000000..a7cda24 --- /dev/null +++ b/internal/domain/common_log.go @@ -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())) diff --git a/internal/domain/company.go b/internal/domain/company.go index ec170be..07d1e25 100644 --- a/internal/domain/company.go +++ b/internal/domain/company.go @@ -11,6 +11,7 @@ import ( type Company struct { ID int64 Name string + Slug string AdminID int64 WalletID int64 DeductedPercentage float32 @@ -27,6 +28,7 @@ type CompanyFilter struct { type GetCompany struct { ID int64 Name string + Slug string AdminID int64 AdminFirstName string AdminLastName string @@ -68,6 +70,7 @@ type UpdateCompanyReq struct { type CompanyRes struct { ID int64 `json:"id" example:"1"` Name string `json:"name" example:"CompanyName"` + Slug string `json:"slug" example:"slug"` AdminID int64 `json:"admin_id" example:"1"` WalletID int64 `json:"wallet_id" example:"1"` DeductedPercentage float32 `json:"deducted_percentage" example:"0.1"` @@ -77,6 +80,7 @@ type CompanyRes struct { type GetCompanyRes struct { ID int64 `json:"id" example:"1"` Name string `json:"name" example:"CompanyName"` + Slug string `json:"slug" example:"slug"` AdminID int64 `json:"admin_id" example:"1"` WalletID int64 `json:"wallet_id" example:"1"` WalletBalance float32 `json:"balance" example:"1"` @@ -92,6 +96,7 @@ func ConvertCompany(company Company) CompanyRes { return CompanyRes{ ID: company.ID, Name: company.Name, + Slug: company.Slug, AdminID: company.AdminID, WalletID: company.WalletID, IsActive: company.IsActive, @@ -103,6 +108,7 @@ func ConvertGetCompany(company GetCompany) GetCompanyRes { return GetCompanyRes{ ID: company.ID, Name: company.Name, + Slug: company.Slug, AdminID: company.AdminID, WalletID: company.WalletID, WalletBalance: company.WalletBalance.Float32(), @@ -112,13 +118,13 @@ func ConvertGetCompany(company GetCompany) GetCompanyRes { AdminFirstName: company.AdminFirstName, AdminLastName: company.AdminLastName, AdminPhoneNumber: company.AdminPhoneNumber, - } } -func ConvertCreateCompany(company CreateCompany) dbgen.CreateCompanyParams { +func ConvertCreateCompany(company CreateCompany, uniqueSlug string) dbgen.CreateCompanyParams { return dbgen.CreateCompanyParams{ Name: company.Name, + Slug: uniqueSlug, AdminID: company.AdminID, WalletID: company.WalletID, DeductedPercentage: company.DeductedPercentage, @@ -129,6 +135,7 @@ func ConvertDBCompany(dbCompany dbgen.Company) Company { return Company{ ID: dbCompany.ID, Name: dbCompany.Name, + Slug: dbCompany.Slug, AdminID: dbCompany.AdminID, WalletID: dbCompany.WalletID, DeductedPercentage: dbCompany.DeductedPercentage, @@ -140,6 +147,7 @@ func ConvertDBCompanyDetails(dbCompany dbgen.CompaniesDetail) GetCompany { return GetCompany{ ID: dbCompany.ID, Name: dbCompany.Name, + Slug: dbCompany.Slug, AdminID: dbCompany.AdminID, WalletID: dbCompany.WalletID, WalletBalance: Currency(dbCompany.Balance), diff --git a/internal/domain/custom_odds.go b/internal/domain/custom_odds.go index 6f9e919..1a0a171 100644 --- a/internal/domain/custom_odds.go +++ b/internal/domain/custom_odds.go @@ -1,6 +1,7 @@ package domain import ( + "encoding/json" "time" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" @@ -9,35 +10,60 @@ import ( type CustomOdd struct { ID int64 OddID int64 - RawOddID int64 + CompanyID int64 EventID string - OddValue float64 + RawOdds []json.RawMessage CreatedAt time.Time } type CreateCustomOdd struct { - OddID int64 - RawOddID int64 - EventID string - OddValue float64 + OddID int64 + CompanyID int64 + EventID string + RawOdds []json.RawMessage } -func ConvertCreateCustomOdd(odd CreateCustomOdd) dbgen.InsertCustomOddParams { - return dbgen.InsertCustomOddParams{ - OddID: odd.OddID, - RawOddID: odd.RawOddID, - EventID: odd.EventID, - OddValue: odd.OddValue, +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{ + OddID: odd.OddID, + CompanyID: odd.CompanyID, + EventID: odd.EventID, + 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{ ID: dbCustomOdd.ID, OddID: dbCustomOdd.OddID, - RawOddID: dbCustomOdd.RawOddID, + CompanyID: dbCustomOdd.CompanyID, EventID: dbCustomOdd.EventID, - OddValue: dbCustomOdd.OddValue, + RawOdds: rawOdds, 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 } diff --git a/internal/domain/disabled_odds.go b/internal/domain/disabled_odds.go index d05b3fc..c4f731f 100644 --- a/internal/domain/disabled_odds.go +++ b/internal/domain/disabled_odds.go @@ -11,6 +11,7 @@ type DisabledOdd struct { OddID int64 RawOddID int64 EventID string + CompanyID int64 CreatedAt time.Time } @@ -18,12 +19,14 @@ type CreateDisabledOdd struct { OddID int64 RawOddID int64 EventID string + CompanyID int64 } func ConvertCreateDisabledOdd(odd CreateDisabledOdd) dbgen.InsertDisabledOddsParams { return dbgen.InsertDisabledOddsParams{ OddID: odd.OddID, EventID: odd.EventID, + CompanyID: odd.CompanyID, RawOddID: odd.RawOddID, } } @@ -34,6 +37,7 @@ func ConvertDBDisabledOdd(dbDisabledOdd dbgen.DisabledOdd) DisabledOdd { OddID: dbDisabledOdd.OddID, RawOddID: dbDisabledOdd.RawOddID, EventID: dbDisabledOdd.EventID, + CompanyID: dbDisabledOdd.CompanyID, CreatedAt: dbDisabledOdd.CreatedAt.Time, } } diff --git a/internal/domain/event.go b/internal/domain/event.go index f241566..0906918 100644 --- a/internal/domain/event.go +++ b/internal/domain/event.go @@ -1,6 +1,11 @@ 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 // Status represents the status of an event. @@ -35,93 +40,408 @@ const ( STATUS_REMOVED EventStatus = "removed" ) -type Event struct { - ID string - SportID int32 - MatchName string - HomeTeam string - AwayTeam string - HomeTeamID int32 - AwayTeamID int32 - HomeKitImage string - AwayKitImage string - LeagueID int32 - LeagueName string - LeagueCC string - StartTime string - Score string - MatchMinute int - TimerStatus string - AddedTime int - MatchPeriod int - IsLive bool - Status string - Source string +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 + 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 + IsMonitored bool + DefaultIsFeatured bool + DefaultIsActive bool + DefaultWinningUpperLimit int32 + Score ValidString + MatchMinute ValidInt + TimerStatus ValidString + AddedTime ValidInt + MatchPeriod ValidInt + IsLive bool + FetchedAt time.Time +} +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 { - 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"` +type CreateEvent struct { + ID string + SportID int32 + MatchName string + HomeTeam string + AwayTeam string + HomeTeamID int64 + AwayTeamID int64 + HomeTeamImage string + AwayTeamImage string + LeagueID int64 + LeagueName string + StartTime time.Time + IsLive bool + Status EventStatus + Source EventSource } -type UpcomingEvent struct { - ID string `json:"id"` // Event ID - SportID int32 `json:"sport_id"` // Sport ID - MatchName string `json:"match_name"` // Match or event name - HomeTeam string `json:"home_team"` // Home team name (if available) - AwayTeam string `json:"away_team"` // Away team name (can be empty/null) - HomeTeamID int32 `json:"home_team_id"` // Home team ID - AwayTeamID int32 `json:"away_team_id"` // Away team ID (can be empty/null) - HomeKitImage string `json:"home_kit_image"` // Kit or image for home team (optional) - AwayKitImage string `json:"away_kit_image"` // Kit or image for away team (optional) - LeagueID int32 `json:"league_id"` // League ID - LeagueName string `json:"league_name"` // League name - LeagueCC string `json:"league_cc"` // League country code - StartTime time.Time `json:"start_time"` // Converted from "time" field in UNIX format - Source string `json:"source"` // bet api provider (bet365, betfair) - Status EventStatus `json:"status"` //Match Status for event - IsFeatured bool `json:"is_featured"` //Whether the event is featured or not - IsMonitored bool `json:"is_monitored"` //Whether the event is monitored or not - IsActive bool `json:"is_active"` //Whether the event is active or not +type EventWithSettingsRes 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"` + IsFeatured bool `json:"is_featured"` + IsMonitored bool `json:"is_monitored"` + 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 { - EventID string - FullScore string - HalfScore string - Status string - Scores map[string]map[string]string + +type EventSettings struct { + CompanyID int64 + EventID string + IsActive ValidBool + IsFeatured ValidBool + WinningUpperLimit ValidInt + UpdatedAt time.Time +} + +type CreateEventSettings struct { + CompanyID int64 + EventID string + IsActive ValidBool + IsFeatured ValidBool + WinningUpperLimit ValidInt } type EventFilter struct { Query ValidString SportID ValidInt32 - LeagueID ValidInt32 + LeagueID ValidInt64 CountryCode ValidString FirstStartTime ValidTime LastStartTime ValidTime - Limit ValidInt64 - Offset ValidInt64 + Limit ValidInt32 + Offset ValidInt32 MatchStatus ValidString // e.g., "upcoming", "in_play", "ended" 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 +} diff --git a/internal/domain/eventres.go b/internal/domain/eventres.go new file mode 100644 index 0000000..89365a9 --- /dev/null +++ b/internal/domain/eventres.go @@ -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"` +} \ No newline at end of file diff --git a/internal/domain/featured_leagues.go b/internal/domain/featured_leagues.go new file mode 100644 index 0000000..8afdce1 --- /dev/null +++ b/internal/domain/featured_leagues.go @@ -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, //Women’s 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 +} diff --git a/internal/domain/league.go b/internal/domain/league.go index c4a2d12..a44ae70 100644 --- a/internal/domain/league.go +++ b/internal/domain/league.go @@ -1,6 +1,24 @@ 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"` Name string `json:"name" example:"BPL"` CountryCode string `json:"cc" example:"uk"` @@ -9,6 +27,49 @@ type League struct { SportID int32 `json:"sport_id" example:"1"` 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 { ID int64 `json:"id" example:"1"` @@ -29,64 +90,87 @@ type LeagueFilter struct { Offset ValidInt64 } -// 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, //Women’s 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 +func ConvertCreateLeague(league CreateLeague) dbgen.InsertLeagueParams { + return dbgen.InsertLeagueParams{ + ID: league.ID, + Name: league.Name, + CountryCode: league.CountryCode.ToPG(), + Bet365ID: league.Bet365ID.ToPG(), + SportID: league.SportID, + DefaultIsActive: league.DefaultIsActive, + DefaultIsFeatured: league.DefaultIsFeatured, + } +} + +func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.InsertLeagueSettingsParams { + return dbgen.InsertLeagueSettingsParams{ + CompanyID: leagueSetting.CompanyID, + LeagueID: leagueSetting.LeagueID, + IsActive: leagueSetting.IsActive.ToPG(), + IsFeatured: leagueSetting.IsFeatured.ToPG(), + } +} + +func ConvertDBBaseLeague(league dbgen.League) BaseLeague { + return BaseLeague{ + ID: league.ID, + Name: league.Name, + CountryCode: ValidString{ + Value: league.CountryCode.String, + Valid: league.CountryCode.Valid, + }, + Bet365ID: ValidInt32{ + Value: league.Bet365ID.Int32, + Valid: league.Bet365ID.Valid, + }, + SportID: league.SportID, + DefaultIsActive: league.DefaultIsActive, + DefaultIsFeatured: league.DefaultIsFeatured, + } +} + +func ConvertDBBaseLeagues(leagues []dbgen.League) []BaseLeague { + result := make([]BaseLeague, len(leagues)) + for i, league := range leagues { + result[i] = ConvertDBBaseLeague(league) + } + return result +} + +func ConvertDBLeagueWithSetting(lws dbgen.LeagueWithSetting) LeagueWithSettings { + return LeagueWithSettings{ + ID: lws.ID, + Name: lws.Name, + CompanyID: lws.CompanyID, + CountryCode: ValidString{ + Value: lws.CountryCode.String, + Valid: lws.CountryCode.Valid, + }, + Bet365ID: ValidInt32{ + Value: lws.Bet365ID.Int32, + Valid: lws.Bet365ID.Valid, + }, + 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(), + } } diff --git a/internal/domain/log_fields.go b/internal/domain/log_fields.go deleted file mode 100644 index 7ce6b1e..0000000 --- a/internal/domain/log_fields.go +++ /dev/null @@ -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()), -} diff --git a/internal/domain/odds.go b/internal/domain/odds.go index 79a015c..fa30718 100644 --- a/internal/domain/odds.go +++ b/internal/domain/odds.go @@ -3,47 +3,184 @@ package domain import ( "encoding/json" "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/jackc/pgx/v5/pgtype" ) -type Market struct { +type CreateOddMarket struct { EventID string - FI string MarketCategory string MarketType string MarketName string MarketID string UpdatedAt time.Time 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"` EventID string `json:"event_id"` - Fi string `json:"fi"` MarketType string `json:"market_type"` MarketName string `json:"market_name"` MarketCategory string `json:"market_category"` 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"` FetchedAt time.Time `json:"fetched_at"` ExpiresAt time.Time `json:"expires_at"` - Source string `json:"source"` IsActive bool `json:"is_active"` } -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 OddMarketSettings struct { + CompanyID int64 + OddMarketID int64 + IsActive ValidBool + CustomRawOdds string + 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 } diff --git a/internal/domain/odds_history.go b/internal/domain/odds_history.go index 10da63a..7413286 100644 --- a/internal/domain/odds_history.go +++ b/internal/domain/odds_history.go @@ -35,18 +35,18 @@ type OddHistoryFilter struct { func ConvertCreateOddHistory(odd CreateOddHistory) dbgen.InsertOddHistoryParams { return dbgen.InsertOddHistoryParams{ - OddID: odd.OddID, - MarketID: odd.MarketID, - RawOddID: odd.RawOddID, - EventID: odd.EventID, - OddValue: odd.OddValue, + OddsMarketID: odd.OddID, + MarketID: odd.MarketID, + RawOddID: odd.RawOddID, + EventID: odd.EventID, + OddValue: odd.OddValue, } } func ConvertDBOddHistory(dbOddHistory dbgen.OddHistory) OddHistory { return OddHistory{ ID: dbOddHistory.ID, - OddID: dbOddHistory.OddID, + OddID: dbOddHistory.OddsMarketID, MarketID: dbOddHistory.MarketID, RawOddID: dbOddHistory.RawOddID, EventID: dbOddHistory.EventID, diff --git a/internal/domain/resultres.go b/internal/domain/resultres.go index 381c86d..064ab61 100644 --- a/internal/domain/resultres.go +++ b/internal/domain/resultres.go @@ -9,47 +9,47 @@ type BaseResultResponse struct { Results []json.RawMessage `json:"results"` } -type LeagueRes struct { +type LeagueResultResponse struct { ID string `json:"id"` Name string `json:"name"` CC string `json:"cc"` } -type Team struct { +type TeamResultResponse struct { ID string `json:"id"` Name string `json:"name"` ImageID ValidInt64 `json:"image_id"` CC string `json:"cc"` } -type Score struct { +type ScoreResultResponse struct { Home string `json:"home"` Away string `json:"away"` } type CommonResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus ValidInt64 `json:"time_status"` - League LeagueRes `json:"league"` - Home Team `json:"home"` - Away Team `json:"away"` - SS string `json:"ss"` + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + TimeStatus ValidInt64 `json:"time_status"` + League LeagueResultResponse `json:"league"` + Home TeamResultResponse `json:"home"` + Away TeamResultResponse `json:"away"` + SS string `json:"ss"` } type FootballResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League LeagueRes `json:"league"` - Home Team `json:"home"` - Away Team `json:"away"` - SS string `json:"ss"` + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + TimeStatus string `json:"time_status"` + League LeagueResultResponse `json:"league"` + Home TeamResultResponse `json:"home"` + Away TeamResultResponse `json:"away"` + SS string `json:"ss"` Scores struct { - FirstHalf Score `json:"1"` - SecondHalf Score `json:"2"` + FirstHalf ScoreResultResponse `json:"1"` + SecondHalf ScoreResultResponse `json:"2"` } `json:"scores"` Stats struct { Attacks []string `json:"attacks"` @@ -78,21 +78,21 @@ type FootballResultResponse struct { } type BasketballResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League LeagueRes `json:"league"` - Home Team `json:"home"` - Away Team `json:"away"` - SS string `json:"ss"` + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + TimeStatus string `json:"time_status"` + League LeagueResultResponse `json:"league"` + Home TeamResultResponse `json:"home"` + Away TeamResultResponse `json:"away"` + SS string `json:"ss"` Scores struct { - FirstQuarter Score `json:"1"` - SecondQuarter Score `json:"2"` - FirstHalf Score `json:"3"` - ThirdQuarter Score `json:"4"` - FourthQuarter Score `json:"5"` - TotalScore Score `json:"7"` + FirstQuarter ScoreResultResponse `json:"1"` + SecondQuarter ScoreResultResponse `json:"2"` + FirstHalf ScoreResultResponse `json:"3"` + ThirdQuarter ScoreResultResponse `json:"4"` + FourthQuarter ScoreResultResponse `json:"5"` + TotalScore ScoreResultResponse `json:"7"` } `json:"scores"` Stats struct { TwoPoints []string `json:"2points"` @@ -125,19 +125,19 @@ type BasketballResultResponse struct { Bet365ID string `json:"bet365_id"` } type IceHockeyResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League LeagueRes `json:"league"` - Home Team `json:"home"` - Away Team `json:"away"` - SS string `json:"ss"` + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + TimeStatus string `json:"time_status"` + League LeagueResultResponse `json:"league"` + Home TeamResultResponse `json:"home"` + Away TeamResultResponse `json:"away"` + SS string `json:"ss"` Scores struct { - FirstPeriod Score `json:"1"` - SecondPeriod Score `json:"2"` - ThirdPeriod Score `json:"3"` - TotalScore Score `json:"5"` + FirstPeriod ScoreResultResponse `json:"1"` + SecondPeriod ScoreResultResponse `json:"2"` + ThirdPeriod ScoreResultResponse `json:"3"` + TotalScore ScoreResultResponse `json:"5"` } `json:"scores"` Stats struct { @@ -222,11 +222,11 @@ type VolleyballResultResponse struct { } `json:"away"` SS string `json:"ss"` Scores struct { - FirstSet Score `json:"1"` - SecondSet Score `json:"2"` - ThirdSet Score `json:"3"` - FourthSet Score `json:"4"` - FivethSet Score `json:"5"` + FirstSet ScoreResultResponse `json:"1"` + SecondSet ScoreResultResponse `json:"2"` + ThirdSet ScoreResultResponse `json:"3"` + FourthSet ScoreResultResponse `json:"4"` + FivethSet ScoreResultResponse `json:"5"` } `json:"scores"` Stats struct { PointsWonOnServe []string `json:"points_won_on_serve"` @@ -290,10 +290,10 @@ type FutsalResultResponse struct { } `json:"away"` SS string `json:"ss"` Scores struct { - FirstPeriod Score `json:"1"` - SecondPeriod Score `json:"2"` - ThirdPeriod Score `json:"3"` - TotalScore Score `json:"4"` + FirstPeriod ScoreResultResponse `json:"1"` + SecondPeriod ScoreResultResponse `json:"2"` + ThirdPeriod ScoreResultResponse `json:"3"` + TotalScore ScoreResultResponse `json:"4"` } `json:"scores"` Events []map[string]string `json:"events"` InplayCreatedAt string `json:"inplay_created_at"` @@ -327,12 +327,12 @@ type NFLResultResponse struct { } `json:"away"` SS string `json:"ss"` Scores struct { - FirstQuarter Score `json:"1"` - SecondQuarter Score `json:"2"` - ThirdQuarter Score `json:"3"` - FourthQuarter Score `json:"4"` - Overtime Score `json:"5"` - TotalScore Score `json:"7"` + FirstQuarter ScoreResultResponse `json:"1"` + SecondQuarter ScoreResultResponse `json:"2"` + ThirdQuarter ScoreResultResponse `json:"3"` + FourthQuarter ScoreResultResponse `json:"4"` + Overtime ScoreResultResponse `json:"5"` + TotalScore ScoreResultResponse `json:"7"` } `json:"scores"` Stats struct { FirstDowns []string `json:"first_downs"` @@ -381,9 +381,9 @@ type RugbyResultResponse struct { } `json:"away"` SS string `json:"ss"` Scores struct { - FirstHalf Score `json:"1"` - SecondHalf Score `json:"2"` - TotalScore Score `json:"7"` + FirstHalf ScoreResultResponse `json:"1"` + SecondHalf ScoreResultResponse `json:"2"` + TotalScore ScoreResultResponse `json:"7"` } `json:"scores"` Stats struct { Tries []string `json:"tries"` @@ -433,17 +433,17 @@ type BaseballResultResponse struct { } `json:"away"` SS string `json:"ss"` Scores struct { - FirstInning Score `json:"1"` - SecondInning Score `json:"2"` - ThirdInning Score `json:"3"` - FourthInning Score `json:"4"` - FifthInning Score `json:"5"` - SixthInning Score `json:"6"` - SeventhInning Score `json:"7"` - EighthInning Score `json:"8"` - NinthInning Score `json:"9"` - ExtraInnings Score `json:"10"` - TotalScore Score `json:"11"` + FirstInning ScoreResultResponse `json:"1"` + SecondInning ScoreResultResponse `json:"2"` + ThirdInning ScoreResultResponse `json:"3"` + FourthInning ScoreResultResponse `json:"4"` + FifthInning ScoreResultResponse `json:"5"` + SixthInning ScoreResultResponse `json:"6"` + SeventhInning ScoreResultResponse `json:"7"` + EighthInning ScoreResultResponse `json:"8"` + NinthInning ScoreResultResponse `json:"9"` + ExtraInnings ScoreResultResponse `json:"10"` + TotalScore ScoreResultResponse `json:"11"` } `json:"scores"` Stats struct { Hits []string `json:"hits"` diff --git a/internal/domain/user.go b/internal/domain/user.go index eb49ec6..ba0099d 100644 --- a/internal/domain/user.go +++ b/internal/domain/user.go @@ -10,24 +10,20 @@ var ( ) type User struct { - ID int64 - FirstName string - LastName string - Email string `json:"email"` - PhoneNumber string `json:"phone_number"` - Password []byte - Role Role - // + ID int64 + FirstName string + LastName string + Email string `json:"email"` + PhoneNumber string `json:"phone_number"` + Password []byte + Role Role EmailVerified bool PhoneVerified bool - // - CreatedAt time.Time - UpdatedAt time.Time - // - SuspendedAt time.Time - Suspended bool - // - CompanyID ValidInt64 + CreatedAt time.Time + UpdatedAt time.Time + SuspendedAt time.Time + Suspended bool + CompanyID ValidInt64 //This should be null } type UserFilter struct { @@ -54,6 +50,7 @@ type RegisterUserReq struct { Otp string ReferralCode string `json:"referral_code"` OtpMedium OtpMedium + CompanyID ValidInt64 } type CreateUserReq struct { FirstName string diff --git a/internal/domain/validtypes.go b/internal/domain/validtypes.go index 709a401..d402517 100644 --- a/internal/domain/validtypes.go +++ b/internal/domain/validtypes.go @@ -115,6 +115,15 @@ func ConvertInt32Ptr(value *int32) ValidInt32 { Valid: true, } } +func ConvertIntPtr(value *int) ValidInt { + if value == nil { + return ValidInt{} + } + return ValidInt{ + Value: *value, + Valid: true, + } +} func ConvertStringPtr(value *string) ValidString { if value == nil { diff --git a/internal/pkgs/helpers/slug.go b/internal/pkgs/helpers/slug.go new file mode 100644 index 0000000..2824c37 --- /dev/null +++ b/internal/pkgs/helpers/slug.go @@ -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 +} diff --git a/internal/repository/auth.go b/internal/repository/auth.go index fcc5f09..367d482 100644 --- a/internal/repository/auth.go +++ b/internal/repository/auth.go @@ -10,8 +10,8 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "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{ Email: pgtype.Text{ String: email, @@ -21,6 +21,7 @@ func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string) (d String: phone, Valid: true, }, + CompanyID: companyID.ToPG(), }) if err != nil { if errors.Is(err, sql.ErrNoRows) { diff --git a/internal/repository/bet.go b/internal/repository/bet.go index b9391f1..2aa520a 100644 --- a/internal/repository/bet.go +++ b/internal/repository/bet.go @@ -80,7 +80,7 @@ func convertDBFlag(flag dbgen.Flag) domain.Flag { return domain.Flag{ ID: flag.ID, BetID: flag.BetID.Int64, - OddID: flag.OddID.Int64, + OddID: flag.OddsMarketID.Int64, Reason: flag.Reason.String, FlaggedAt: flag.FlaggedAt.Time, Resolved: flag.Resolved.Bool, @@ -157,7 +157,7 @@ func (s *Store) CreateFlag(ctx context.Context, flag domain.CreateFlagReq) (doma Int64: flag.BetID, Valid: flag.BetID != 0, }, - OddID: pgtype.Int8{ + OddsMarketID: pgtype.Int8{ Int64: flag.OddID, Valid: flag.OddID != 0, }, diff --git a/internal/repository/company.go b/internal/repository/company.go index a877b23..dc440e9 100644 --- a/internal/repository/company.go +++ b/internal/repository/company.go @@ -2,14 +2,36 @@ package repository import ( "context" + "database/sql" + "errors" "fmt" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers" "github.com/jackc/pgx/v5/pgtype" ) - 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 { return domain.Company{}, err } @@ -56,6 +78,15 @@ func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany return domain.ConvertDBCompanyDetails(dbCompany), nil } +func (s *Store) GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error) { + dbCompanyID, err := s.queries.GetCompanyIDUsingSlug(ctx, slug) + + if err != nil { + return 0, err + } + return dbCompanyID, nil +} + func (s *Store) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) { dbCompany, err := s.queries.UpdateCompany(ctx, domain.ConvertUpdateCompany(company)) diff --git a/internal/repository/custom_odds.go b/internal/repository/custom_odds.go index 6b2506f..3400878 100644 --- a/internal/repository/custom_odds.go +++ b/internal/repository/custom_odds.go @@ -1,5 +1,105 @@ package repository -import "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// import ( +// "context" -func (s *Store) InsertCustomOdds(ctx context.Context, odd domain.ConvertCreateCustomOdd) (domain.) \ No newline at end of file +// 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 +// } diff --git a/internal/repository/event.go b/internal/repository/event.go index fb43d79..d0798d7 100644 --- a/internal/repository/event.go +++ b/internal/repository/event.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "math" - "time" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" @@ -13,92 +12,29 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -func (s *Store) SaveEvent(ctx context.Context, e domain.Event) error { - parsedTime, err := time.Parse(time.RFC3339, e.StartTime) - 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) SaveEvent(ctx context.Context, e domain.CreateEvent) error { + return s.queries.InsertEvent(ctx, domain.ConvertCreateEvent(e)) } -func (s *Store) SaveUpcomingEvent(ctx context.Context, e domain.UpcomingEvent) error { - return s.queries.InsertUpcomingEvent(ctx, dbgen.InsertUpcomingEventParams{ - 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: e.StartTime, Valid: true}, - Source: pgtype.Text{String: e.Source, Valid: true}, - }) + +func (s *Store) InsertEventSettings(ctx context.Context, eventSetting domain.CreateEventSettings) error { + return s.queries.InsertEventSettings(ctx, domain.ConvertCreateEventSettings(eventSetting)) } func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) { return s.queries.ListLiveEvents(ctx) } -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) if err != nil { return nil, 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, - IsMonitored: e.IsMonitored, - IsActive: e.IsActive, - } - } - return upcomingEvents, nil + return domain.ConvertDBEvents(events), nil } -func (s *Store) GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, error) { - events, err := s.queries.GetExpiredUpcomingEvents(ctx, pgtype.Text{ +func (s *Store) GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, error) { + events, err := s.queries.GetExpiredEvents(ctx, pgtype.Text{ String: filter.MatchStatus.Value, Valid: filter.MatchStatus.Valid, }) @@ -106,166 +42,98 @@ func (s *Store) GetExpiredUpcomingEvents(ctx context.Context, filter domain.Even return nil, 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, - } - } - return upcomingEvents, nil + return domain.ConvertDBEvents(events), 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{ - LeagueID: pgtype.Int4{ - Int32: int32(filter.LeagueID.Value), - Valid: filter.LeagueID.Valid, - }, - SportID: pgtype.Int4{ - Int32: int32(filter.SportID.Value), - Valid: filter.SportID.Valid, - }, - 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, - }, + 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 { 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{ - LeagueID: pgtype.Int4{ - Int32: int32(filter.LeagueID.Value), - Valid: filter.LeagueID.Valid, - }, - SportID: pgtype.Int4{ - Int32: int32(filter.SportID.Value), - 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, - }, + LeagueID: filter.LeagueID.ToPG(), + SportID: filter.SportID.ToPG(), + Query: filter.Query.ToPG(), + FirstStartTime: filter.FirstStartTime.ToPG(), + LastStartTime: filter.LastStartTime.ToPG(), + CountryCode: filter.CountryCode.ToPG(), }) if err != nil { return nil, 0, err } 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 { - return domain.UpcomingEvent{}, err + return nil, 0, err } - return domain.UpcomingEvent{ - ID: event.ID, - SportID: event.SportID.Int32, - MatchName: event.MatchName.String, - HomeTeam: event.HomeTeam.String, - AwayTeam: event.AwayTeam.String, - HomeTeamID: event.HomeTeamID.Int32, - AwayTeamID: event.AwayTeamID.Int32, - HomeKitImage: event.HomeKitImage.String, - AwayKitImage: event.AwayKitImage.String, - LeagueID: event.LeagueID.Int32, - LeagueName: event.LeagueName.String, - LeagueCC: event.LeagueCc.String, - StartTime: event.StartTime.Time.UTC(), - Source: event.Source.String, - Status: domain.EventStatus(event.Status.String), - IsFeatured: event.IsFeatured, - IsActive: event.IsActive, - IsMonitored: event.IsMonitored, - }, nil + totalCount, err := s.queries.GetTotalCompanyEvents(ctx, dbgen.GetTotalCompanyEventsParams{ + CompanyID: companyID, + LeagueID: filter.LeagueID.ToPG(), + SportID: filter.SportID.ToPG(), + Query: filter.Query.ToPG(), + FirstStartTime: filter.FirstStartTime.ToPG(), + LastStartTime: filter.LastStartTime.ToPG(), + CountryCode: filter.CountryCode.ToPG(), + }) + if err != nil { + return nil, 0, err + } + + numberOfPages := math.Ceil(float64(totalCount) / float64(filter.Limit.Value)) + return domain.ConvertDBEventWithSettings(events), int64(numberOfPages), nil +} +func (s *Store) GetUpcomingEventByID(ctx context.Context, ID string) (domain.BaseEvent, error) { + event, err := s.queries.GetUpcomingByID(ctx, ID) + if err != 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 { params := dbgen.UpdateMatchResultParams{ Score: pgtype.Text{String: fullScore, Valid: true}, - Status: pgtype.Text{String: string(status), Valid: true}, + Status: string(status), ID: eventID, } @@ -279,11 +147,8 @@ func (s *Store) UpdateFinalScore(ctx context.Context, eventID, fullScore string, func (s *Store) UpdateEventStatus(ctx context.Context, eventID string, status domain.EventStatus) error { params := dbgen.UpdateMatchResultParams{ - Status: pgtype.Text{ - String: string(status), - Valid: true, - }, - ID: eventID, + Status: string(status), + ID: eventID, } err := s.queries.UpdateMatchResult(ctx, params) @@ -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) { 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 { err := s.queries.DeleteEvent(ctx, eventID) if err != nil { diff --git a/internal/repository/league.go b/internal/repository/league.go index fa6f870..f003fd9 100644 --- a/internal/repository/league.go +++ b/internal/repository/league.go @@ -8,36 +8,18 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -func (s *Store) SaveLeague(ctx context.Context, l domain.League) error { - return s.queries.InsertLeague(ctx, dbgen.InsertLeagueParams{ - 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) SaveLeague(ctx context.Context, league domain.CreateLeague) error { + return s.queries.InsertLeague(ctx, domain.ConvertCreateLeague(league)) } -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{ - CountryCode: pgtype.Text{ - String: filter.CountryCode.Value, - 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, - }, + CountryCode: filter.CountryCode.ToPG(), + SportID: filter.SportID.ToPG(), Limit: pgtype.Int4{ Int32: int32(filter.Limit.Value), Valid: filter.Limit.Valid, @@ -51,85 +33,38 @@ func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ( return nil, err } - leagues := make([]domain.League, len(l)) - 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 + return domain.ConvertDBBaseLeagues(l), nil } -func (s *Store) GetFeaturedLeagues(ctx context.Context) ([]domain.League, error) { - l, err := s.queries.GetFeaturedLeagues(ctx) +func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error) { + 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 { return nil, err } - leagues := make([]domain.League, len(l)) - 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 + return domain.ConvertDBLeagueWithSettings(l), nil } -func (s *Store) CheckLeagueSupport(ctx context.Context, leagueID int64) (bool, error) { - return s.queries.CheckLeagueSupport(ctx, leagueID) -} - -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) CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) { + return s.queries.CheckLeagueSupport(ctx, dbgen.CheckLeagueSupportParams{ + LeagueID: leagueID, + CompanyID: companyID, }) } func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error { - err := s.queries.UpdateLeague(ctx, dbgen.UpdateLeagueParams{ - 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 + return s.queries.UpdateLeague(ctx, domain.ConvertUpdateLeague(league)) } diff --git a/internal/repository/odds.go b/internal/repository/odds.go index ac68ff9..373de20 100644 --- a/internal/repository/odds.go +++ b/internal/repository/odds.go @@ -3,7 +3,6 @@ package repository import ( "context" "encoding/json" - "fmt" "os" "strconv" @@ -14,57 +13,26 @@ import ( "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 { return nil } - for _, item := range m.Odds { - var name string - var oddsVal float64 + params, err := domain.ConvertCreateOddMarket(m) - if m.Source == "bwin" { - nameValue := getMap(item["name"]) - name = getString(nameValue["value"]) - oddsVal = getFloat(item["odds"]) - } else { - name = getString(item["name"]) - oddsVal = getConvertedFloat(item["odds"]) - } - handicap := getString(item["handicap"]) + if err != nil { + return err + } - 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) - fmt.Printf("Inserting Non Live Odd") - if err != nil { - _ = writeFailedMarketLog(m, err) - continue - } + err = s.queries.InsertOddsMarket(ctx, params) + if err != nil { + _ = writeFailedMarketLog(m, err) + return err } return nil } -func writeFailedMarketLog(m domain.Market, err error) error { +func writeFailedMarketLog(m domain.CreateOddMarket, err error) error { logDir := "logs" logFile := logDir + "/failed_markets.log" @@ -79,9 +47,9 @@ func writeFailedMarketLog(m domain.Market, err error) error { defer f.Close() entry := struct { - Time string `json:"time"` - Error string `json:"error"` - Record domain.Market `json:"record"` + Time string `json:"time"` + Error string `json:"error"` + Record domain.CreateOddMarket `json:"record"` }{ Time: time.Now().Format(time.RFC3339), Error: err.Error(), @@ -93,196 +61,130 @@ func writeFailedMarketLog(m domain.Market, err error) error { return writeErr } -func (s *Store) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { - odds, err := s.queries.GetPrematchOdds(ctx) +func (s *Store) GetAllOdds(ctx context.Context, filter domain.OddMarketFilter) ([]domain.OddMarket, error) { + rows, err := s.queries.GetAllOdds(ctx, dbgen.GetAllOddsParams{ + Offset: filter.Offset.ToPG(), + Limit: filter.Limit.ToPG(), + }) if err != nil { return nil, err } - domainOdds := make([]domain.Odd, len(odds)) - for i, odd := range odds { - 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: 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, - } + domainOdds, err := domain.ConvertDBOddMarkets(rows) + + if err != nil { + return nil, err } return domainOdds, nil } -func (s *Store) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) { - rows, err := s.queries.GetALLPrematchOdds(ctx) +func (s *Store) GetAllOddsWithSettings(ctx context.Context, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) { + odds, err := s.queries.GetAllOddsWithSettings(ctx, dbgen.GetAllOddsWithSettingsParams{ + CompanyID: companyID, + Offset: filter.Offset.ToPG(), + Limit: filter.Limit.ToPG(), + }) if err != nil { return nil, err } - domainOdds := make([]domain.Odd, len(rows)) - for i, row := range rows { - domainOdds[i] = domain.Odd{ - // ID: int64(row.ID), - 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, - } + domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds) + + if err != nil { + return nil, err } return domainOdds, nil } -func (s *Store) GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.RawOddsByMarketID, error) { - params := dbgen.GetOddsByMarketIDParams{ - MarketID: pgtype.Text{String: marketID, Valid: true}, - Fi: pgtype.Text{String: upcomingID, Valid: true}, - } +func (s *Store) GetOddsByMarketID(ctx context.Context, marketID string, eventID string) (domain.OddMarket, error) { - odds, err := s.queries.GetOddsByMarketID(ctx, params) + odds, err := s.queries.GetOddsByMarketID(ctx, dbgen.GetOddsByMarketIDParams{ + MarketID: marketID, + EventID: eventID, + }) if err != nil { - return domain.RawOddsByMarketID{}, err + return domain.OddMarket{}, err } - var rawOdds []json.RawMessage - if err := json.Unmarshal(odds.RawOdds, &rawOdds); err != nil { - return domain.RawOddsByMarketID{}, err - } + convertedOdd, err := domain.ConvertDBOddMarket(odds) - return domain.RawOddsByMarketID{ - ID: int64(odds.ID), - 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 - }(), - FetchedAt: odds.FetchedAt.Time, - ExpiresAt: odds.ExpiresAt.Time, - }, nil + if err != nil { + return domain.OddMarket{}, err + } + return convertedOdd, nil } -func (s *Store) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit domain.ValidInt32, offset domain.ValidInt32) ([]domain.Odd, error) { - odds, err := s.queries.GetPaginatedPrematchOddsByUpcomingID(ctx, dbgen.GetPaginatedPrematchOddsByUpcomingIDParams{ - ID: upcomingID, - Limit: limit.ToPG(), - Offset: offset.ToPG(), +func (s *Store) GetOddsWithSettingsByMarketID(ctx context.Context, marketID string, eventID string, companyID int64) (domain.OddMarketWithSettings, error) { + + odds, err := s.queries.GetOddsWithSettingsByMarketID(ctx, dbgen.GetOddsWithSettingsByMarketIDParams{ + MarketID: marketID, + 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 { return nil, err } // Map the results to domain.Odd - domainOdds := make([]domain.Odd, len(odds)) - for i, odd := range odds { - var rawOdds []json.RawMessage - 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, - } + domainOdds, err := domain.ConvertDBOddMarkets(odds) + if err != nil { + return nil, err } 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 { return nil, err } // Map the results to domain.Odd - domainOdds := make([]domain.Odd, len(odds)) - for i, odd := range odds { - var rawOdds []json.RawMessage - 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, - } + domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds) + if err != nil { + return nil, err } return domainOdds, nil } func (s *Store) DeleteOddsForEvent(ctx context.Context, eventID string) error { - return s.queries.DeleteOddsForEvent(ctx, pgtype.Text{ - String: eventID, - Valid: true, - }) + return s.queries.DeleteOddsForEvent(ctx, eventID) } func getString(v interface{}) string { diff --git a/internal/repository/user.go b/internal/repository/user.go index 4387be4..703e745 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -46,6 +46,8 @@ func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int6 Time: time.Now(), Valid: true, }, + CompanyID: user.CompanyID.ToPG(), + Suspended: user.Suspended, }) if err != nil { return domain.User{}, err @@ -57,6 +59,15 @@ func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int6 Email: userRes.Email.String, PhoneNumber: userRes.PhoneNumber.String, 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 } 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) { query := dbgen.SearchUserByNameOrPhoneParams{ - Column1: pgtype.Text{ + CompanyID: companyID.ToPG(), + Column2: pgtype.Text{ String: searchString, Valid: true, }, - CompanyID: pgtype.Int8{ - Int64: companyID.Value, - Valid: companyID.Valid, - }, } if role != nil { - query.Role = pgtype.Text{ String: string(*role), Valid: true, @@ -340,7 +347,7 @@ func (s *Store) DeleteUser(ctx context.Context, id int64) error { } 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{ PhoneNumber: pgtype.Text{ @@ -352,6 +359,7 @@ func (s *Store) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string Valid: email != "", }, + CompanyID: companyID.ToPG(), }) if err != nil { @@ -360,10 +368,13 @@ func (s *Store) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string return row.EmailExists, row.PhoneExists, nil } -func (s *Store) GetUserByEmail(ctx context.Context, email string) (domain.User, error) { - user, err := s.queries.GetUserByEmail(ctx, pgtype.Text{ - String: email, - Valid: true, +func (s *Store) GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error) { + user, err := s.queries.GetUserByEmail(ctx, dbgen.GetUserByEmailParams{ + Email: pgtype.Text{ + String: email, + Valid: true, + }, + CompanyID: companyID.ToPG(), }) if err != nil { 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, }, nil } -func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error) { - user, err := s.queries.GetUserByPhone(ctx, pgtype.Text{ - String: phoneNum, - Valid: true, +func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error) { + user, err := s.queries.GetUserByPhone(ctx, dbgen.GetUserByPhoneParams{ + PhoneNumber: pgtype.Text{ + String: phoneNum, + Valid: true, + }, + CompanyID: companyID.ToPG(), }) if err != nil { if errors.Is(err, sql.ErrNoRows) { diff --git a/internal/services/authentication/impl.go b/internal/services/authentication/impl.go index 2760a63..8bebc32 100644 --- a/internal/services/authentication/impl.go +++ b/internal/services/authentication/impl.go @@ -26,8 +26,8 @@ type LoginSuccess struct { CompanyID domain.ValidInt64 } -func (s *Service) Login(ctx context.Context, email, phone string, password string) (LoginSuccess, error) { - user, err := s.userStore.GetUserByEmailPhone(ctx, email, phone) +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, companyID) if err != nil { return LoginSuccess{}, err } diff --git a/internal/services/authentication/port.go b/internal/services/authentication/port.go index 2728241..0c313e3 100644 --- a/internal/services/authentication/port.go +++ b/internal/services/authentication/port.go @@ -7,7 +7,7 @@ import ( ) 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 { CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error diff --git a/internal/services/bet/service.go b/internal/services/bet/service.go index 1ac84bf..ad44770 100644 --- a/internal/services/bet/service.go +++ b/internal/services/bet/service.go @@ -133,7 +133,7 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI return domain.CreateBetOutcome{}, ErrEventHasNotEnded } - odds, err := s.prematchSvc.GetRawOddsByMarketID(ctx, marketIDStr, eventIDStr) + odds, err := s.prematchSvc.GetOddsByMarketID(ctx, marketIDStr, eventIDStr) if err != nil { s.mongoLogger.Error("failed to get raw odds by market ID", zap.Int64("event_id", eventID), @@ -581,7 +581,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string, var newOdds []domain.CreateBetOutcome var totalOdds float32 = 1 - markets, err := s.prematchSvc.GetPrematchOddsByUpcomingID(ctx, eventID) + markets, err := s.prematchSvc.GetOddsByEventID(ctx, eventID, domain.OddMarketWithEventFilter{}) if err != nil { s.logger.Error("failed to get odds for event", "event id", eventID, "error", err) s.mongoLogger.Error("failed to get odds for event", @@ -603,7 +603,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string, return nil, 0, fmt.Errorf("empty odds or event %v", eventID) } - var selectedMarkets []domain.Odd + var selectedMarkets []domain.OddMarket numMarkets = min(numMarkets, len(markets)) for i := 0; i < numMarkets; i++ { randomIndex := random.Intn(len(markets)) @@ -714,7 +714,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string, 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 @@ -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 - var selectedUpcomingEvents []domain.UpcomingEvent + var selectedUpcomingEvents []domain.BaseEvent numEventsPerBet := min(random.Intn(4)+1, len(events)) //Eliminate the option of 0 for i := 0; i < int(numEventsPerBet); i++ { diff --git a/internal/services/company/port.go b/internal/services/company/port.go index 992f69f..10bfa70 100644 --- a/internal/services/company/port.go +++ b/internal/services/company/port.go @@ -11,6 +11,7 @@ type CompanyStore interface { GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) + GetCompanyIDBySlug(ctx context.Context, slug string) (int64, error) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) DeleteCompany(ctx context.Context, id int64) error diff --git a/internal/services/company/service.go b/internal/services/company/service.go index ce82da8..a396a10 100644 --- a/internal/services/company/service.go +++ b/internal/services/company/service.go @@ -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) { 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) { return s.companyStore.SearchCompanyByName(ctx, name) diff --git a/internal/services/event/port.go b/internal/services/event/port.go index 1cd67c8..e02be83 100644 --- a/internal/services/event/port.go +++ b/internal/services/event/port.go @@ -7,16 +7,18 @@ import ( ) type Service interface { - FetchLiveEvents(ctx context.Context) error + // FetchLiveEvents(ctx context.Context) error FetchUpcomingEvents(ctx context.Context) error - GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) - GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, error) - GetPaginatedUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.UpcomingEvent, int64, error) - GetUpcomingEventByID(ctx context.Context, ID string) (domain.UpcomingEvent, error) + GetAllUpcomingEvents(ctx context.Context) ([]domain.BaseEvent, error) + GetExpiredUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, error) + GetPaginatedUpcomingEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) + GetUpcomingEventByID(ctx context.Context, ID string) (domain.BaseEvent, error) // GetAndStoreMatchResult(ctx context.Context, eventID string) error UpdateFinalScore(ctx context.Context, eventID, fullScore 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) 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 } diff --git a/internal/services/event/service.go b/internal/services/event/service.go index f381887..ab227bc 100644 --- a/internal/services/event/service.go +++ b/internal/services/event/service.go @@ -32,162 +32,162 @@ func New(token string, store *repository.Store, mongoLogger *zap.Logger) Service } } -func (s *service) FetchLiveEvents(ctx context.Context) error { - var wg sync.WaitGroup - urls := []struct { - name string - source string - }{ - {"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/1xbet/inplay?sport_id=%d&token=%s", "1xbet"}, - } +// func (s *service) FetchLiveEvents(ctx context.Context) error { +// var wg sync.WaitGroup +// urls := []struct { +// name string +// source string +// }{ +// {"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/1xbet/inplay?sport_id=%d&token=%s", "1xbet"}, +// } - for _, url := range urls { - wg.Add(1) +// for _, url := range urls { +// wg.Add(1) - go func() { - defer wg.Done() - s.fetchLiveEvents(ctx, url.name, url.source) - }() - } - wg.Wait() - return nil -} +// go func() { +// defer wg.Done() +// s.fetchLiveEvents(ctx, url.name, url.source) +// }() +// } +// wg.Wait() +// return nil +// } -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} +// 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} - var wg sync.WaitGroup +// var wg sync.WaitGroup - for _, sportID := range sportIDs { - wg.Add(1) - go func(sportID int) { - defer wg.Done() +// for _, sportID := range sportIDs { +// wg.Add(1) +// go func(sportID int) { +// defer wg.Done() - url := fmt.Sprintf(url, sportID, s.token) - resp, err := http.Get(url) - if err != nil { - fmt.Printf(" Failed request for sport_id=%d: %v\n", sportID, err) - return - } - defer resp.Body.Close() +// url := fmt.Sprintf(url, sportID, s.token) +// resp, err := http.Get(url) +// if err != nil { +// fmt.Printf(" Failed request for sport_id=%d: %v\n", sportID, err) +// return +// } +// defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) +// body, _ := io.ReadAll(resp.Body) - events := []domain.Event{} - switch source { - case "bet365": - events = handleBet365prematch(body, sportID, source) - case "betfair": - events = handleBetfairprematch(body, sportID, source) - case "1xbet": - // betfair and 1xbet have the same result structure - events = handleBetfairprematch(body, sportID, source) - } +// events := []domain.Event{} +// switch source { +// case "bet365": +// events = handleBet365prematch(body, sportID, source) +// case "betfair": +// events = handleBetfairprematch(body, sportID, source) +// case "1xbet": +// // betfair and 1xbet have the same result structure +// events = handleBetfairprematch(body, sportID, source) +// } - for _, event := range events { - if err := s.store.SaveEvent(ctx, event); err != nil { - fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) - } - } - }(sportID) - } +// for _, event := range events { +// if err := s.store.SaveEvent(ctx, event); err != nil { +// fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) +// } +// } +// }(sportID) +// } - wg.Wait() - fmt.Println("All live events fetched and stored.") - return nil +// wg.Wait() +// fmt.Println("All live events fetched and stored.") +// return nil -} +// } -func handleBet365prematch(body []byte, sportID int, source string) []domain.Event { - var data struct { - Success int `json:"success"` - Results [][]map[string]interface{} `json:"results"` - } +// func handleBet365prematch(body []byte, sportID int, source string) []domain.Event { +// var data struct { +// Success int `json:"success"` +// Results [][]map[string]interface{} `json:"results"` +// } - events := []domain.Event{} - 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)) - return events - } +// events := []domain.Event{} +// 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)) +// return events +// } - for _, group := range data.Results { - for _, ev := range group { - if getString(ev["type"]) != "EV" { - continue - } +// for _, group := range data.Results { +// for _, ev := range group { +// if getString(ev["type"]) != "EV" { +// continue +// } - event := domain.Event{ - ID: getString(ev["ID"]), - SportID: int32(sportID), - MatchName: getString(ev["NA"]), - Score: getString(ev["SS"]), - MatchMinute: getInt(ev["TM"]), - TimerStatus: getString(ev["TT"]), - HomeTeamID: getInt32(ev["HT"]), - AwayTeamID: getInt32(ev["AT"]), - HomeKitImage: getString(ev["K1"]), - AwayKitImage: getString(ev["K2"]), - LeagueName: getString(ev["CT"]), - LeagueID: getInt32(ev["C2"]), - LeagueCC: getString(ev["CB"]), - StartTime: time.Now().UTC().Format(time.RFC3339), - IsLive: true, - Status: "live", - MatchPeriod: getInt(ev["MD"]), - AddedTime: getInt(ev["TA"]), - Source: source, - } +// event := domain.Event{ +// ID: getString(ev["ID"]), +// SportID: int32(sportID), +// MatchName: getString(ev["NA"]), +// Score: getString(ev["SS"]), +// MatchMinute: getInt(ev["TM"]), +// TimerStatus: getString(ev["TT"]), +// HomeTeamID: getInt32(ev["HT"]), +// AwayTeamID: getInt32(ev["AT"]), +// HomeKitImage: getString(ev["K1"]), +// AwayKitImage: getString(ev["K2"]), +// LeagueName: getString(ev["CT"]), +// LeagueID: getInt32(ev["C2"]), +// LeagueCC: getString(ev["CB"]), +// StartTime: time.Now().UTC().Format(time.RFC3339), +// IsLive: true, +// Status: "live", +// MatchPeriod: getInt(ev["MD"]), +// AddedTime: getInt(ev["TA"]), +// Source: source, +// } - events = append(events, event) - } - } +// events = append(events, event) +// } +// } - return events -} +// return events +// } -func handleBetfairprematch(body []byte, sportID int, source string) []domain.Event { - var data struct { - Success int `json:"success"` - Results []map[string]interface{} `json:"results"` - } +// func handleBetfairprematch(body []byte, sportID int, source string) []domain.Event { +// var data struct { +// Success int `json:"success"` +// Results []map[string]interface{} `json:"results"` +// } - events := []domain.Event{} - 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)) - return events - } +// events := []domain.Event{} +// 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)) +// return events +// } - for _, ev := range data.Results { - homeRaw, _ := ev["home"].(map[string]interface{}) - awayRaw, _ := ev["home"].(map[string]interface{}) +// for _, ev := range data.Results { +// homeRaw, _ := ev["home"].(map[string]interface{}) +// awayRaw, _ := ev["home"].(map[string]interface{}) - event := domain.Event{ - ID: getString(ev["id"]), - SportID: int32(sportID), - TimerStatus: getString(ev["time_status"]), - HomeTeamID: getInt32(homeRaw["id"]), - AwayTeamID: getInt32(awayRaw["id"]), - StartTime: time.Now().UTC().Format(time.RFC3339), - IsLive: true, - Status: "live", - Source: source, - } +// event := domain.Event{ +// ID: getString(ev["id"]), +// SportID: int32(sportID), +// TimerStatus: getString(ev["time_status"]), +// HomeTeamID: getInt32(homeRaw["id"]), +// AwayTeamID: getInt32(awayRaw["id"]), +// StartTime: time.Now().UTC().Format(time.RFC3339), +// IsLive: true, +// Status: "live", +// Source: source, +// } - events = append(events, event) - } +// events = append(events, event) +// } - return events -} +// return events +// } func (s *service) FetchUpcomingEvents(ctx context.Context) error { var wg sync.WaitGroup urls := []struct { 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/1xbet/upcoming?sport_id=%d&token=%s&page=%d", "1xbet"}, } @@ -205,38 +205,38 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error { 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} - // 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 { var totalPages int = 1 var page int = 0 - var limit int = 200 var count int = 0 var skippedLeague []string 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 { page = page + 1 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)", source, sportID, sportIndex+1, len(sportIDs), page, totalPages) + eventLogger := logger.With( + zap.Int("page", page), + zap.Int("total_pages", totalPages), + ) resp, err := http.Get(url) 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("total_pages", totalPages), - zap.Error(err), - ) + eventLogger.Error("Failed to fetch event data for page", zap.Error(err)) continue } defer resp.Body.Close() @@ -244,56 +244,31 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur body, err := io.ReadAll(resp.Body) if err != nil { - s.mongoLogger.Error( - "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), - ) + eventLogger.Error("Failed to read event response body", zap.Error(err)) continue } - var data domain.BetResult + var data domain.B365UpcomingRes if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { - s.mongoLogger.Error( - "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), - ) + eventLogger.Error("Failed to parse event json data", zap.Error(err)) continue } for _, ev := range data.Results { startUnix, err := strconv.ParseInt(ev.Time, 10, 64) + dataLogger := eventLogger.With( + zap.String("time", ev.Time), + zap.String("leagueID", ev.League.ID), + zap.String("leagueName", ev.League.Name), + ) if err != nil { - s.mongoLogger.Error( - "Invalid time", - zap.String("time", ev.Time), - zap.String("source", source), - zap.Int("sport_id", sportID), - zap.Int("page", page), - zap.Int("total_pages", totalPages), - zap.Error(err), - ) + dataLogger.Error("Invalid time", zap.Error(err)) continue } leagueID, err := strconv.ParseInt(ev.League.ID, 10, 64) if err != nil { - s.mongoLogger.Error( - "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), - ) + dataLogger.Error("Invalid league id", zap.Error(err)) continue } @@ -301,87 +276,67 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur // no this its fine to keep it here // but change the league id to bet365 id later //Automatically feature the league if its in the list - err = s.store.SaveLeague(ctx, domain.League{ - ID: leagueID, - Name: ev.League.Name, - IsActive: true, - IsFeatured: slices.Contains(domain.FeaturedLeagues, leagueID), - SportID: convertInt32(ev.SportID), + err = s.store.SaveLeague(ctx, domain.CreateLeague{ + ID: leagueID, + Name: ev.League.Name, + DefaultIsActive: true, + DefaultIsFeatured: slices.Contains(domain.FeaturedLeagues, leagueID), + SportID: convertInt32(ev.SportID), }) if err != nil { - s.mongoLogger.Error( - "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), - ) + dataLogger.Error("error while saving league", zap.Error(err)) continue } - if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil { - s.mongoLogger.Warn( - "Skipping league", - zap.String("league", ev.League.Name), - zap.Bool("is_supported", supported), - zap.Error(err), - ) - skippedLeague = append(skippedLeague, ev.League.Name) - continue - } + // Since the system is multi-vendor now, no events are going to be skipped + // if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil { + // dataLogger.Warn( + // "Skipping league", + // zap.Bool("is_supported", supported), + // zap.Error(err), + // ) + // skippedLeague = append(skippedLeague, ev.League.Name) + // continue + // } - event := domain.UpcomingEvent{ - ID: ev.ID, - SportID: convertInt32(ev.SportID), - MatchName: "", - HomeTeam: ev.Home.Name, - AwayTeam: "", // handle nil safely - HomeTeamID: convertInt32(ev.Home.ID), - AwayTeamID: 0, - HomeKitImage: "", - AwayKitImage: "", - LeagueID: convertInt32(ev.League.ID), - LeagueName: ev.League.Name, - LeagueCC: "", - StartTime: time.Unix(startUnix, 0).UTC(), - Source: source, + event := domain.CreateEvent{ + ID: ev.ID, + SportID: convertInt32(ev.SportID), + MatchName: "", + HomeTeam: ev.Home.Name, + AwayTeam: "", // handle nil safely + HomeTeamID: convertInt64(ev.Home.ID), + AwayTeamID: 0, + HomeTeamImage: "", + AwayTeamImage: "", + LeagueID: convertInt64(ev.League.ID), + LeagueName: ev.League.Name, + StartTime: time.Unix(startUnix, 0).UTC(), + Source: source, + IsLive: false, + Status: domain.STATUS_PENDING, } if ev.Away != nil { + dataLogger.Info("event away is empty") 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 } + ok, err := s.CheckAndInsertEventHistory(ctx, event) - if err := s.CheckAndInsertEventHistory(ctx, event); err != nil { - s.mongoLogger.Error( - "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), - ) + if err != nil { + dataLogger.Error("failed to check and insert event history", 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 { - s.mongoLogger.Error( - "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), - ) + dataLogger.Error("failed to save upcoming event", zap.Error(err)) } totalEvents += 1 } @@ -391,7 +346,7 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur totalPages = data.Pager.Total / data.Pager.PerPage - if count >= limit { + if count >= pageLimit { break } if page > totalPages { @@ -401,7 +356,7 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur } s.mongoLogger.Info( "Successfully fetched upcoming events", - zap.String("source", source), + zap.String("source", string(source)), zap.Int("totalEvents", totalEvents), zap.Int("sport_id", sportID), 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) + eventLogger := s.mongoLogger.With( + zap.String("eventID", event.ID), + zap.Int64("leagueID", event.LeagueID), + zap.String("leagueName", event.LeagueName), + zap.Int32("sport_id", event.SportID), + ) + if err != nil { - s.mongoLogger.Error( - "failed to get event is_monitored", - zap.String("eventID", event.ID), - zap.Int32("leagueID", event.LeagueID), - zap.String("leagueName", event.LeagueName), - zap.Int32("sport_id", event.SportID), - zap.Error(err), - ) + eventLogger.Error("failed to get event is_monitored", zap.Error(err)) + return false, err } if !isEventMonitored { - return nil + return false, nil } oldEvent, err := s.GetUpcomingEventByID(ctx, event.ID) if err != nil { - s.mongoLogger.Error( - "failed to get event by id", - zap.String("eventID", event.ID), - zap.Int32("leagueID", event.LeagueID), - zap.String("leagueName", event.LeagueName), - zap.Int32("sport_id", event.SportID), - zap.Error(err), - ) + eventLogger.Error("failed to get event by id", zap.Error(err)) + return false, err } if oldEvent.Status != event.Status { @@ -451,20 +401,14 @@ func (s *service) CheckAndInsertEventHistory(ctx context.Context, event domain.U }) if err != nil { - s.mongoLogger.Error( - "failed to get event by id", - 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 + eventLogger.Error("failed to get event by id", zap.Error(err)) + return false, err } + + return true, nil } - return nil + return false, nil } func getString(v interface{}) string { @@ -494,19 +438,25 @@ func convertInt32(num string) int32 { } 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) } -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) } -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) } -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) } @@ -517,48 +467,20 @@ func (s *service) UpdateEventStatus(ctx context.Context, eventID string, 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) { return s.store.IsEventMonitored(ctx, eventID) } 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 { -// url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%s", s.token, eventID) +func (s *service) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) { + return s.store.GetEventsWithSettings(ctx, companyID, filter) +} -// resp, err := http.Get(url) -// if err != nil { -// return fmt.Errorf("failed to fetch result: %w", err) -// } -// defer resp.Body.Close() - -// 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 -// } +func (s *service) GetEventWithSettingByID(ctx context.Context, ID string, companyID int64) (domain.EventWithSettings, error) { + return s.store.GetEventWithSettingByID(ctx, ID, companyID) +} +func (s *service) UpdateEventSettings(ctx context.Context, event domain.CreateEventSettings) error { + return s.store.UpdateEventSettings(ctx, event) +} diff --git a/internal/services/league/port.go b/internal/services/league/port.go index 1f49632..b203c4f 100644 --- a/internal/services/league/port.go +++ b/internal/services/league/port.go @@ -7,9 +7,10 @@ import ( ) type Service interface { - SaveLeague(ctx context.Context, l domain.League) error - GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.League, error) - GetFeaturedLeagues(ctx context.Context) ([]domain.League, error) - SetLeagueActive(ctx context.Context, leagueId int64, isActive bool) error + SaveLeague(ctx context.Context, league domain.CreateLeague) error + SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error + GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, error) + GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error) + CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error } diff --git a/internal/services/league/service.go b/internal/services/league/service.go index 275dfb5..d82b38f 100644 --- a/internal/services/league/service.go +++ b/internal/services/league/service.go @@ -17,20 +17,24 @@ func New(store *repository.Store) Service { } } -func (s *service) SaveLeague(ctx context.Context, l domain.League) error { - return s.store.SaveLeague(ctx, l) +func (s *service) SaveLeague(ctx context.Context, league domain.CreateLeague) error { + 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) } -func (s *service) GetFeaturedLeagues(ctx context.Context) ([]domain.League, error) { - return s.store.GetFeaturedLeagues(ctx) +func (s *service) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, error) { + return s.store.GetAllLeaguesByCompany(ctx, companyID, filter) } -func (s *service) SetLeagueActive(ctx context.Context, leagueId int64, isActive bool) error { - return s.store.SetLeagueActive(ctx, leagueId, isActive) +func (s *service) CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) { + return s.store.CheckLeagueSupport(ctx, leagueID, companyID) } func (s *service) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error { diff --git a/internal/services/odds/custom_odds.go b/internal/services/odds/custom_odds.go new file mode 100644 index 0000000..f3c7d5d --- /dev/null +++ b/internal/services/odds/custom_odds.go @@ -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) +// } diff --git a/internal/services/odds/disabled_odd.go b/internal/services/odds/disabled_odd.go new file mode 100644 index 0000000..cd3dd06 --- /dev/null +++ b/internal/services/odds/disabled_odd.go @@ -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) +} + diff --git a/internal/services/odds/odds_history.go b/internal/services/odds/odds_history.go new file mode 100644 index 0000000..48b8421 --- /dev/null +++ b/internal/services/odds/odds_history.go @@ -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) +} \ No newline at end of file diff --git a/internal/services/odds/port.go b/internal/services/odds/port.go index 7387b4d..6014ed0 100644 --- a/internal/services/odds/port.go +++ b/internal/services/odds/port.go @@ -11,13 +11,32 @@ type Service interface { FetchNonLiveOdds(ctx context.Context) error FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error) ParseOddSections(ctx context.Context, res json.RawMessage, sportID int64) (domain.ParseOddSectionsRes, error) - GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) - GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.Odd, error) - GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit domain.ValidInt64, offset domain.ValidInt64) ([]domain.Odd, error) - GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) + GetPrematchOdds(ctx context.Context, eventID string) ([]domain.OddMarket, error) + GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.OddMarket, error) + GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit domain.ValidInt64, offset domain.ValidInt64) ([]domain.OddMarket, error) + GetALLPrematchOdds(ctx context.Context) ([]domain.OddMarket, error) GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.RawOddsByMarketID, error) DeleteOddsForEvent(ctx context.Context, eventID string) error + + // Odd History InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error) GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) + + // Disabling Odds + InsertDisabledOdd(ctx context.Context, odd domain.CreateDisabledOdd) (domain.DisabledOdd, error) + GetAllDisabledOdds(ctx context.Context) ([]domain.DisabledOdd, error) + GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (domain.DisabledOdd, error) + GetDisabledOddByID(ctx context.Context, id int64) (domain.DisabledOdd, error) + DeleteDisabledOddsByID(ctx context.Context, id int64) error + DeleteDisabledOddsByRawOddID(ctx context.Context, id int64) error + + // Custom Odds + // InsertCustomOdds(ctx context.Context, odd domain.CreateCustomOdd) (domain.CustomOdd, error) + // GetAllCustomOdds(ctx context.Context, filter domain.CustomOddFilter) ([]domain.CustomOdd, error) + // GetCustomOddByID(ctx context.Context, id int64) (domain.CustomOdd, error) + // GetCustomOddByOddID(ctx context.Context, oddId int64, companyID int64) (domain.CustomOdd, error) + // DeleteCustomOddByID(ctx context.Context, id int64) error + // DeleteCustomOddsByOddID(ctx context.Context, oddId int64, companyID int64) error + // DeleteCustomOddByEventID(ctx context.Context, eventID string) error } diff --git a/internal/services/odds/service.go b/internal/services/odds/service.go index 178da5b..afa9e11 100644 --- a/internal/services/odds/service.go +++ b/internal/services/odds/service.go @@ -164,115 +164,117 @@ func (s *ServiceImpl) fetchBet365Odds(ctx context.Context) error { return nil } -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 - // so instead of having event and odds fetched separetly event will also be fetched along with the odds - sportIds := []int{4, 12, 7} - for _, sportId := range sportIds { - 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) - if err != nil { - s.mongoLogger.Error( - "Failed to create request for sportId", - zap.Int("sportID", sportId), - zap.Error(err), - ) - continue - } +// 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 +// // so instead of having event and odds fetched separetly event will also be fetched along with the odds +// sportIds := []int{4, 12, 7} +// for _, sportId := range sportIds { +// 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) +// if err != nil { +// s.mongoLogger.Error( +// "Failed to create request for sportId", +// zap.Int("sportID", sportId), +// zap.Error(err), +// ) +// continue +// } - resp, err := s.client.Do(req) - if err != nil { - s.mongoLogger.Error( - "Failed to fetch request for sportId", - zap.Int("sportID", sportId), - zap.Error(err), - ) - continue - } - defer resp.Body.Close() +// resp, err := s.client.Do(req) +// if err != nil { +// s.mongoLogger.Error( +// "Failed to fetch request for sportId", +// zap.Int("sportID", sportId), +// zap.Error(err), +// ) +// continue +// } +// defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - s.mongoLogger.Error( - "Failed to read response body for sportId", - zap.Int("sportID", sportId), - zap.Error(err), - ) - continue - } +// body, err := io.ReadAll(resp.Body) +// if err != nil { +// s.mongoLogger.Error( +// "Failed to read response body for sportId", +// zap.Int("sportID", sportId), +// zap.Error(err), +// ) +// continue +// } - var data struct { - Success int `json:"success"` - Results []map[string]interface{} `json:"results"` - } +// var data struct { +// Success int `json:"success"` +// Results []map[string]interface{} `json:"results"` +// } - 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)) - s.mongoLogger.Error( - "Failed to decode BWin response body", - zap.Int("sportID", sportId), - zap.Error(err), - ) - continue - } +// 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)) +// s.mongoLogger.Error( +// "Failed to decode BWin response body", +// zap.Int("sportID", sportId), +// zap.Error(err), +// ) +// continue +// } - for _, res := range data.Results { - if getInt(res["Id"]) == -1 { - continue - } +// for _, res := range data.Results { +// if getInt(res["Id"]) == -1 { +// continue +// } - event := domain.Event{ - ID: strconv.Itoa(getInt(res["Id"])), - SportID: int32(getInt(res["SportId"])), - LeagueID: int32(getInt(res["LeagueId"])), - LeagueName: getString(res["Leaguename"]), - HomeTeam: getString(res["HomeTeam"]), - HomeTeamID: int32(getInt(res["HomeTeamId"])), - AwayTeam: getString(res["AwayTeam"]), - AwayTeamID: int32(getInt(res["AwayTeamId"])), - StartTime: time.Now().UTC().Format(time.RFC3339), - TimerStatus: "1", - IsLive: true, - Status: "live", - Source: "bwin", - } +// event := domain.CreateEvent{ +// ID: strconv.Itoa(getInt(res["Id"])), +// SportID: int32(getInt(res["SportId"])), +// LeagueID: int64(getInt(res["LeagueId"])), +// LeagueName: getString(res["Leaguename"]), +// HomeTeam: getString(res["HomeTeam"]), +// HomeTeamID: int64(getInt(res["HomeTeamId"])), +// AwayTeam: getString(res["AwayTeam"]), +// AwayTeamID: int64(getInt(res["AwayTeamId"])), +// StartTime: time.Now().UTC(), +// IsLive: true, +// Status: domain.STATUS_IN_PLAY, +// Source: domain.EVENT_SOURCE_BWIN, +// MatchName: "", +// HomeTeamImage: "", +// AwayTeamImage: "", +// } - if err := s.store.SaveEvent(ctx, event); err != nil { - fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) - s.mongoLogger.Error( - "Could not store live event", - zap.Int("sportID", sportId), - zap.String("eventID", event.ID), - zap.Error(err), - ) - continue - } +// if err := s.store.SaveEvent(ctx, event); err != nil { +// fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) +// s.mongoLogger.Error( +// "Could not store live event", +// zap.Int("sportID", sportId), +// zap.String("eventID", event.ID), +// zap.Error(err), +// ) +// continue +// } - for _, market := range []string{"Markets, optionMarkets"} { - for _, m := range getMapArray(res[market]) { - name := getMap(m["name"]) - marketName := getString(name["value"]) +// for _, market := range []string{"Markets, optionMarkets"} { +// for _, m := range getMapArray(res[market]) { +// name := getMap(m["name"]) +// marketName := getString(name["value"]) - market := domain.Market{ - EventID: event.ID, - MarketID: getString(m["id"]), - MarketCategory: getString(m["category"]), - MarketName: marketName, - Source: "bwin", - } +// market := domain.CreateOddMarket{ +// EventID: event.ID, +// MarketID: getString(m["id"]), +// MarketCategory: getString(m["category"]), +// MarketName: marketName, - 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) { @@ -545,7 +547,7 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName } // Check if the market id is a string - marketIDint := market.ID.Int64 + marketIDint := market.ID.Value // if err != nil { // s.mongoLogger.Error( // "Invalid market id", @@ -579,9 +581,8 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName continue } - marketRecord := domain.Market{ + marketRecord := domain.CreateOddMarket{ EventID: eventID, - FI: fi, MarketCategory: sectionName, MarketType: marketType, MarketName: market.Name, @@ -589,7 +590,6 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName UpdatedAt: updatedAt, Odds: marketOdds, // bwin won't reach this code so bet365 is hardcoded for now - Source: "bet365", } if err := s.CheckAndInsertOddHistory(ctx, marketRecord); err != nil { @@ -603,7 +603,7 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName continue } - err = s.store.SaveNonLiveMarket(ctx, marketRecord) + err = s.store.SaveOddMarket(ctx, marketRecord) if err != nil { s.mongoLogger.Error( "failed to save market", @@ -623,57 +623,38 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName 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) + marketLogger := s.mongoLogger.With( + zap.String("market_id", market.MarketID), + zap.String("market_name", market.MarketName), + zap.String("eventID", market.EventID), + ) if err != nil { - s.mongoLogger.Error( - "failed to get is_monitored", - zap.String("market_id", market.MarketID), - zap.String("market_name", market.Name), - zap.String("eventID", market.EventID), - zap.Error(err), - ) + marketLogger.Error("failed to get is_monitored", zap.Error(err)) } if !isEventMonitored { 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 { - s.mongoLogger.Error( - "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), - ) + marketLogger.Error("failed to get raw odds by market id", zap.Error(err)) return err } if len(oldOdds.RawOdds) != len(market.Odds) { - s.mongoLogger.Error( - "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), - ) + marketLogger.Error("new odds data does not match old odds data", zap.Error(err)) return fmt.Errorf("new odds data does not match old odds data") } oldRawOdds, err := convertRawMessage(oldOdds.RawOdds) if err != nil { - s.mongoLogger.Error( - "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), - ) + marketLogger.Error("failed to convert raw odds to map", zap.Error(err)) return err } @@ -698,7 +679,7 @@ func (s *ServiceImpl) CheckAndInsertOddHistory(ctx context.Context, market domai s.mongoLogger.Error( "failed to insert odd history", zap.String("market_id", market.MarketID), - zap.String("market_name", market.Name), + zap.String("market_name", market.MarketName), zap.String("eventID", market.EventID), zap.Int64("odd_id", oldOdds.ID), zap.Int("raw_odd_id", newRawOddID), @@ -716,29 +697,37 @@ func (s *ServiceImpl) CheckAndInsertOddHistory(ctx context.Context, market domai return nil } -func (s *ServiceImpl) GetPrematchOdds(ctx context.Context, eventID string) ([]domain.Odd, error) { - return s.store.GetPrematchOdds(ctx, eventID) +func (s *ServiceImpl) GetAllOdds(ctx context.Context, filter domain.OddMarketFilter) ([]domain.OddMarket, error) { + return s.store.GetAllOdds(ctx, filter) } -func (s *ServiceImpl) GetALLPrematchOdds(ctx context.Context) ([]domain.Odd, error) { - return s.store.GetALLPrematchOdds(ctx) +func (s *ServiceImpl) GetAllOddsWithSettings(ctx context.Context, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) { + return s.store.GetAllOddsWithSettings(ctx, companyID, filter) } -func (s *ServiceImpl) GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.RawOddsByMarketID, error) { - rows, err := s.store.GetRawOddsByMarketID(ctx, marketID, upcomingID) +func (s *ServiceImpl) GetOddsByMarketID(ctx context.Context, marketID string, eventID string) (domain.OddMarket, error) { + rows, err := s.store.GetOddsByMarketID(ctx, marketID, eventID) 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 } -func (s *ServiceImpl) GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.Odd, error) { - return s.store.GetPrematchOddsByUpcomingID(ctx, upcomingID) +func (s *ServiceImpl) GetOddsByEventID(ctx context.Context, upcomingID string, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) { + return s.store.GetOddsByEventID(ctx, upcomingID, filter) } -func (s *ServiceImpl) GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit, offset domain.ValidInt32) ([]domain.Odd, error) { - return s.store.GetPaginatedPrematchOddsByUpcomingID(ctx, upcomingID, limit, offset) +func (s *ServiceImpl) GetOddsWithSettingsByEventID(ctx context.Context, upcomingID string, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) { + return s.store.GetOddsWithSettingsByEventID(ctx, upcomingID, companyID, filter) } func (s *ServiceImpl) DeleteOddsForEvent(ctx context.Context, eventID string) error { diff --git a/internal/services/result/service.go b/internal/services/result/service.go index cc2138a..ad1f1c5 100644 --- a/internal/services/result/service.go +++ b/internal/services/result/service.go @@ -70,89 +70,57 @@ var ( 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 outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID, true) + logger := s.mongoLogger.With( + zap.Int64("eventID", eventID), + zap.Int64("sportID", sportID), + ) + if err != nil { - s.mongoLogger.Error( - "Failed to get pending bet outcomes", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + 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) } - - // if len(outcomes) == 0 { - // s.mongoLogger.Info( - // "No bets have been placed on event", - // zap.Int64("eventID", eventID), - // ) - - // } for _, outcome := range outcomes { + outcomeLogger := logger.With( + zap.Int64("outcome_id", outcome.ID), + 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()) { - s.mongoLogger.Warn( - "Outcome is not expired yet", - zap.Int64("eventID", eventID), - zap.Int64("outcome_id", outcome.ID), - zap.Error(err), - ) + outcomeLogger.Warn("Outcome is not expired yet", zap.Error(err)) return fmt.Errorf("Outcome has not expired yet") } parseResult, err := s.parseResult(resultRes, outcome, sportID) if err != nil { - s.mongoLogger.Error( - "Failed to parse result", - zap.Int64("eventID", eventID), - zap.Int64("outcome_id", outcome.ID), - zap.Error(err), - ) + outcomeLogger.Error("Failed to parse result", zap.Error(err)) return err } outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, parseResult.Status) if err != nil { - s.mongoLogger.Error( - "Failed to update bet outcome status", - zap.Int64("eventID", eventID), - zap.Int64("outcome_id", outcome.ID), - zap.Error(err), - ) + outcomeLogger.Error("Failed to update bet outcome status", zap.Error(err)) return err } if outcome.Status == domain.OUTCOME_STATUS_ERROR || outcome.Status == domain.OUTCOME_STATUS_PENDING { - s.mongoLogger.Error( - "Outcome has been updated to pending or error", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + outcomeLogger.Error("Outcome has been updated to pending or error", zap.Error(err)) return fmt.Errorf("Error while updating outcome") } status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID) if err != nil { if err != bet.ErrOutcomesNotCompleted { - s.mongoLogger.Error( - "Failed to check bet outcome for bet", - zap.Int64("eventID", eventID), - zap.Int64("betID", outcome.BetID), - zap.Error(err), - ) + outcomeLogger.Error("Failed to check bet outcome for bet", zap.Error(err)) } return err } - s.mongoLogger.Info( - "Updating bet status", - zap.Int64("eventID", eventID), - zap.Int64("betID", outcome.BetID), - zap.String("status", status.String()), - zap.Int64("eventID", eventID), - ) + outcomeLogger.Info("Updating bet status", zap.String("status", status.String())) + err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status) if err != nil { - s.mongoLogger.Error( - "Failed to update bet status", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + outcomeLogger.Error("Failed to update bet status", zap.Error(err)) return err } } @@ -268,46 +236,32 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error { var resultLog domain.CreateResultLog var resultStatusBets domain.ResultStatusBets for _, event := range events { - + eventLogger := s.mongoLogger.With( + zap.String("eventID", event.ID), + ) eventID, err := strconv.ParseInt(event.ID, 10, 64) if err != nil { - s.mongoLogger.Error( - "Failed to parse Event ID", - zap.String("eventID", event.ID), - zap.Error(err), - ) + eventLogger.Error("Failed to parse Event ID", zap.Error(err)) continue } result, err := s.fetchResult(ctx, eventID) if err != nil { if err == ErrEventIsNotActive { - s.mongoLogger.Warn( - "Event is not active", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + eventLogger.Warn("Event is not active", zap.Error(err)) continue } - s.mongoLogger.Error( - "Failed to fetch result", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + eventLogger.Error("Failed to fetch result", zap.Error(err)) continue } var commonResp domain.CommonResultResponse if err := json.Unmarshal(result.Results[0], &commonResp); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal common result", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + eventLogger.Error("Failed to unmarshal common result", zap.Error(err)) continue } - timeStatusParsed := commonResp.TimeStatus.Int64 + timeStatusParsed := commonResp.TimeStatus.Value // if err != nil { // s.mongoLogger.Error( // "Failed to parse time status", @@ -325,6 +279,10 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error { // continue // } // 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 { case int64(domain.TIME_STATUS_NOT_STARTED), int64(domain.TIME_STATUS_IN_PLAY): resultLog.StatusNotFinishedCount += 1 @@ -342,20 +300,12 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error { err = s.repo.DeleteEvent(ctx, event.ID) if err != nil { - s.mongoLogger.Error( - "Failed to remove event", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + commonRespLogger.Error("Failed to remove event", zap.Error(err)) continue } err = s.repo.DeleteOddsForEvent(ctx, event.ID) if err != nil { - s.mongoLogger.Error( - "Failed to remove odds for event", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) continue } resultLog.RemovedCount += 1 @@ -393,37 +343,21 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error { } sportID, err := strconv.ParseInt(commonResp.SportID, 10, 64) if err != nil { - s.mongoLogger.Error( - "Failed to parse sport id", - zap.String("sportID", commonResp.SportID), - zap.Error(err), - ) + commonRespLogger.Error("Failed to parse sport id", zap.Error(err)) continue } err = s.UpdateResultForOutcomes(ctx, eventID, result.Results[0], sportID) if err != nil { - s.mongoLogger.Error( - "Error while updating result for event", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + commonRespLogger.Error("Error while updating result for event", zap.Error(err)) } err = s.repo.DeleteEvent(ctx, event.ID) if err != nil { - s.mongoLogger.Error( - "Failed to remove event", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + commonRespLogger.Error("Failed to remove event", zap.Error(err)) continue } err = s.repo.DeleteOddsForEvent(ctx, event.ID) if err != nil { - s.mongoLogger.Error( - "Failed to remove odds for event", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) continue } resultLog.RemovedCount += 1 @@ -452,20 +386,12 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error { err = s.repo.DeleteEvent(ctx, event.ID) if err != nil { - s.mongoLogger.Error( - "Failed to remove event", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + commonRespLogger.Error("Failed to remove event", zap.Error(err)) continue } err = s.repo.DeleteOddsForEvent(ctx, event.ID) if err != nil { - s.mongoLogger.Error( - "Failed to remove odds for event", - zap.Int64("eventID", eventID), - zap.Error(err), - ) + commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) continue } resultLog.RemovedCount += 1 @@ -718,7 +644,7 @@ func (s *Service) CheckAndUpdateExpiredEvents(ctx context.Context) (int64, error var eventStatus domain.EventStatus // TODO Change event status to int64 enum - timeStatus := commonResp.TimeStatus.Int64 + timeStatus := commonResp.TimeStatus.Value // if err != nil { // s.mongoLogger.Error( // "Invalid time status", @@ -958,7 +884,7 @@ func (s *Service) GetResultsForEvent(ctx context.Context, eventID string) (json. outcomes := make([]domain.BetOutcome, 0) for _, section := range parsedOddSections.Sections { for _, market := range section.Sp { - marketIDint := market.ID.Int64 + marketIDint := market.ID.Value // if err != nil { // s.mongoLogger.Error( // "Invalid market id", @@ -1135,147 +1061,80 @@ func (s *Service) parseResult(resultResp json.RawMessage, outcome domain.BetOutc var result domain.CreateResult 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 { case domain.FOOTBALL: result, err = s.parseFootball(resultResp, outcome) if err != nil { - s.mongoLogger.Error( - "Failed to parse football", - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - zap.Error(err), - ) + s.mongoLogger.Error("Failed to parse football", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.BASKETBALL: result, err = s.parseBasketball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "Failed to parse basketball", - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - zap.Error(err), - ) + s.mongoLogger.Error("Failed to parse basketball", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.ICE_HOCKEY: result, err = s.parseIceHockey(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "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), - ) + s.mongoLogger.Error("Failed to parse ice hockey", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.CRICKET: result, err = s.parseCricket(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "Failed to parse cricket", - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - zap.Error(err), - ) + s.mongoLogger.Error("Failed to parse cricket", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.VOLLEYBALL: result, err = s.parseVolleyball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "Failed to parse volleyball", - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - zap.Error(err), - ) + s.mongoLogger.Error("Failed to parse volleyball", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.DARTS: result, err = s.parseDarts(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "Failed to parse darts", - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - zap.Error(err), - ) + s.mongoLogger.Error("Failed to parse darts", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.FUTSAL: result, err = s.parseFutsal(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "Failed to parse futsal", - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - zap.Error(err), - ) + s.mongoLogger.Error("Failed to parse futsal", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.AMERICAN_FOOTBALL: result, err = s.parseNFL(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "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), - ) + s.mongoLogger.Error("Failed to parse american football", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.RUGBY_UNION: result, err = s.parseRugbyUnion(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "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), - ) + s.mongoLogger.Error("Failed to parse rugby_union", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.RUGBY_LEAGUE: result, err = s.parseRugbyLeague(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "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), - ) + s.mongoLogger.Error("Failed to parse rugby_league", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } case domain.BASEBALL: result, err = s.parseBaseball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) if err != nil { - s.mongoLogger.Error( - "Failed to parse baseball", - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - zap.Error(err), - ) + s.mongoLogger.Error("Failed to parse baseball", append(logFields, zap.Error(err))...) return domain.CreateResult{}, err } default: - s.mongoLogger.Error( - "Unsupported sport", - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - zap.Error(err), - ) + s.mongoLogger.Error("Unsupported sport", append(logFields, zap.Error(err))...) return domain.CreateResult{}, fmt.Errorf("unsupported sport: %v", sportID) } diff --git a/internal/services/user/port.go b/internal/services/user/port.go index 0f2c6fa..8625f35 100644 --- a/internal/services/user/port.go +++ b/internal/services/user/port.go @@ -19,9 +19,9 @@ type UserStore interface { UpdateUserCompany(ctx context.Context, id int64, companyID int64) error UpdateUserSuspend(ctx context.Context, id int64, status bool) error DeleteUser(ctx context.Context, id int64) error - CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) - GetUserByEmail(ctx context.Context, email string) (domain.User, error) - GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error) + CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error) + GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (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) UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64) error // identifier verified email or phone diff --git a/internal/services/user/register.go b/internal/services/user/register.go index 4c8c003..d09a7a7 100644 --- a/internal/services/user/register.go +++ b/internal/services/user/register.go @@ -7,17 +7,17 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) -func (s *Service) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) { // email,phone,error - return s.userStore.CheckPhoneEmailExist(ctx, phoneNum, email) +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, 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 // check if user exists switch medium { case domain.OtpMediumEmail: - _, err = s.userStore.GetUserByEmail(ctx, sentTo) + _, err = s.userStore.GetUserByEmail(ctx, sentTo, companyID) case domain.OtpMediumSms: - _, err = s.userStore.GetUserByPhone(ctx, sentTo) + _, err = s.userStore.GetUserByPhone(ctx, sentTo, companyID) } if err != nil && err != domain.ErrUserNotFound { @@ -68,6 +68,7 @@ func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterU Role: domain.RoleCustomer, EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail, PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms, + CompanyID: registerReq.CompanyID, } // create the user and mark otp as used user, err := s.userStore.CreateUser(ctx, userR, otp.ID, false) diff --git a/internal/services/user/reset.go b/internal/services/user/reset.go index 8834cb0..0b5cc0f 100644 --- a/internal/services/user/reset.go +++ b/internal/services/user/reset.go @@ -8,15 +8,15 @@ import ( "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 // check if user exists switch medium { case domain.OtpMediumEmail: - _, err = s.userStore.GetUserByEmail(ctx, sentTo) + _, err = s.userStore.GetUserByEmail(ctx, sentTo, companyID) case domain.OtpMediumSms: - _, err = s.userStore.GetUserByPhone(ctx, sentTo) + _, err = s.userStore.GetUserByPhone(ctx, sentTo, companyID) } if err != nil { diff --git a/internal/web_server/handlers/auth_handler.go b/internal/web_server/handlers/auth_handler.go index 6d92b98..a5f215e 100644 --- a/internal/web_server/handlers/auth_handler.go +++ b/internal/web_server/handlers/auth_handler.go @@ -38,8 +38,13 @@ type loginCustomerRes struct { // @Failure 400 {object} response.APIResponse // @Failure 401 {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 { + 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 if err := c.BodyParser(&req); err != nil { 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) } - 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 { switch { case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound): @@ -153,8 +159,13 @@ type loginAdminRes struct { // @Failure 400 {object} response.APIResponse // @Failure 401 {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 { + 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 if err := c.BodyParser(&req); err != nil { 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) } - 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 { switch { 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) } +// 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 { AccessToken string `json:"access_token" validate:"required" example:""` RefreshToken string `json:"refresh_token" validate:"required" example:""` diff --git a/internal/web_server/handlers/event_handler.go b/internal/web_server/handlers/event_handler.go index 2f8fe93..e503b29 100644 --- a/internal/web_server/handlers/event_handler.go +++ b/internal/web_server/handlers/event_handler.go @@ -29,30 +29,28 @@ import ( func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error { page := c.QueryInt("page", 1) pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt64{ - Value: int64(pageSize), + limit := domain.ValidInt32{ + Value: int32(pageSize), Valid: true, } - offset := domain.ValidInt64{ - Value: int64(page - 1), + offset := domain.ValidInt32{ + Value: int32(page - 1), Valid: true, } leagueIDQuery := c.Query("league_id") - var leagueID domain.ValidInt32 + var leagueID domain.ValidInt64 if leagueIDQuery != "" { - leagueIDInt, err := strconv.Atoi(leagueIDQuery) + leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64) if err != nil { - h.mongoLoggerSvc.Error("invalid league id", + domain.BadRequestLogger.Error("invalid league id", zap.String("league_id", leagueIDQuery), - zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), - zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "invalid league id") } - leagueID = domain.ValidInt32{ - Value: int32(leagueIDInt), + leagueID = domain.ValidInt64{ + Value: leagueIDInt, Valid: true, } } @@ -61,11 +59,9 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error { if sportIDQuery != "" { sportIDint, err := strconv.Atoi(sportIDQuery) if err != nil { - h.mongoLoggerSvc.Info("invalid sport id", + domain.BadRequestLogger.Info("invalid sport id", zap.String("sportID", sportIDQuery), - zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), - zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") } @@ -86,11 +82,9 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error { if firstStartTimeQuery != "" { firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery) if err != nil { - h.mongoLoggerSvc.Info("invalid start_time format", + domain.BadRequestLogger.Info("invalid start_time format", zap.String("first_start_time", firstStartTimeQuery), - zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), - zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") } @@ -105,11 +99,9 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error { if lastStartTimeQuery != "" { lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery) 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.Int("status_code", fiber.StatusBadRequest), zap.Error(err), - zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") } @@ -130,10 +122,9 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error { if isFeaturedQuery != "" { isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery) if err != nil { - h.mongoLoggerSvc.Error("Failed to parse isFeatured", - zap.Int("status_code", fiber.StatusBadRequest), + domain.BadRequestLogger.Error("Failed to parse isFeatured", + zap.String("is_featured", isFeaturedQuery), zap.Error(err), - zap.Time("timestamp", time.Now()), ) 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) if err != nil { - h.mongoLoggerSvc.Error("Failed to retrieve all upcoming events", - zap.Int("status_code", fiber.StatusInternalServerError), + domain.InternalServerErrorLogger.Error("Failed to retrieve all upcoming events", zap.Error(err), - zap.Time("timestamp", time.Now()), ) 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)) } @@ -176,11 +324,11 @@ type TopLeaguesRes struct { } type TopLeague struct { - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - LeagueCC string `json:"league_cc"` - LeagueSportID int32 `json:"league_sport_id"` - Events []domain.UpcomingEvent `json:"events"` + LeagueID int64 `json:"league_id"` + LeagueName string `json:"league_name"` + LeagueCC string `json:"league_cc"` + LeagueSportID int32 `json:"league_sport_id"` + Events []domain.EventWithSettingsRes `json:"events"` // Total int64 `json:"total"` } @@ -191,43 +339,51 @@ type TopLeague struct { // @Produce json // @Success 200 {array} TopLeague // @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 { + 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 { - h.mongoLoggerSvc.Error("Error while fetching top leagues", - zap.Int("status_code", fiber.StatusInternalServerError), + domain.InternalServerErrorLogger.Error("Error while fetching top leagues", + zap.Int64("company_id", companyID.Value), zap.Error(err), - zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } var topLeague []TopLeague = make([]TopLeague, 0, len(leagues)) for _, league := range leagues { - events, _, err := h.eventSvc.GetPaginatedUpcomingEvents( - c.Context(), domain.EventFilter{ - LeagueID: domain.ValidInt32{ - Value: int32(league.ID), + events, _, err := h.eventSvc.GetEventsWithSettings( + c.Context(), companyID.Value, domain.EventFilter{ + LeagueID: domain.ValidInt64{ + Value: league.ID, Valid: true, }, }) 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.Int("status_code", fiber.StatusInternalServerError), + zap.Int64("company_id", companyID.Value), zap.Error(err), - zap.Time("timestamp", time.Now()), ) } topLeague = append(topLeague, TopLeague{ LeagueID: league.ID, LeagueName: league.Name, - LeagueCC: league.CountryCode, + LeagueCC: league.CountryCode.Value, 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") if id == "" { - h.mongoLoggerSvc.Info("Failed to parse event id", - zap.String("id", id), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) + domain.BadRequestLogger.Info("Failed to parse event id", zap.String("id", id)) return fiber.NewError(fiber.StatusBadRequest, "Missing id") } event, err := h.eventSvc.GetUpcomingEventByID(c.Context(), id) 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.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), - zap.Time("timestamp", time.Now()), ) 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) if err != nil { - h.mongoLoggerSvc.Error("Failed to update event status", + domain.InternalServerErrorLogger.Error("Failed to update event status", zap.String("EventID", eventID), - zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), - zap.Time("timestamp", time.Now()), ) 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 { - Featured bool `json:"is_featured" example:"true"` +type UpdateEventSettingsReq struct { + Featured *bool `json:"is_featured" example:"true"` + IsActive *bool `json:"is_active" example:"true"` + WinningUpperLimit *int `json:"winning_upper_limit" example:"10000"` } -// UpdateEventFeatured godoc -// @Summary update the event featured -// @Description Update the event featured +// UpdateEventSettings godoc +// @Summary update the event settings +// @Description Update the event settings // @Tags event // @Accept json // @Produce json @@ -321,44 +511,53 @@ type UpdateEventFeaturedReq struct { // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse -// @Router /api/v1/events/{id}/flag [put] -func (h *Handler) UpdateEventFeatured(c *fiber.Ctx) error { - eventID := c.Params("id") +// @Router /api/v1/{tenant_slug}/events/{id}/settings [put] +func (h *Handler) UpdateEventSettings(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 UpdateEventFeaturedReq + eventID := c.Params("id") + var req UpdateEventSettingsReq 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.Int("status_code", fiber.StatusBadRequest), zap.Error(err), - zap.Time("timestamp", time.Now()), ) 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) if !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } - h.mongoLoggerSvc.Error("Failed to update event featured", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), + domain.BadRequestLogger.Error("Failed to update event featured", + append(logFields, zap.String("errMsg", 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 { - h.mongoLoggerSvc.Error("Failed to update event featured", - zap.String("eventID", eventID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + domain.InternalServerErrorLogger.Error("Failed to update event featured", append(logFields, zap.Error(err))...) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } diff --git a/internal/web_server/handlers/leagues.go b/internal/web_server/handlers/leagues.go index 09b63a1..222d95d 100644 --- a/internal/web_server/handlers/leagues.go +++ b/internal/web_server/handlers/leagues.go @@ -3,7 +3,6 @@ package handlers import ( "fmt" "strconv" - "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" @@ -51,11 +50,9 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error { if sportIDQuery != "" { sportIDint, err := strconv.Atoi(sportIDQuery) if err != nil { - h.mongoLoggerSvc.Info("invalid sport id", + domain.BadRequestLogger.Info("invalid sport id", zap.String("sport_id", sportIDQuery), - zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), - zap.Time("timestamp", time.Now()), ) 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{ CountryCode: countryCode, IsActive: isActive, @@ -75,16 +89,106 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error { if err != nil { fmt.Printf("Error fetching league %v \n", err) - h.mongoLoggerSvc.Error("Failed to get all leagues", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), + 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) } +// 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 { IsActive bool `json:"is_active"` } @@ -100,8 +204,13 @@ type SetLeagueActiveReq struct { // @Success 200 {object} response.APIResponse // @Failure 400 {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 { + 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") if leagueIdStr == "" { 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") } + queryLogFields := []zap.Field{ + zap.Int64("company_id", companyID.Value), + zap.Bool("company_valid", companyID.Valid), + zap.String("league_id", leagueIdStr), + } + var req SetLeagueActiveReq if err := c.BodyParser(&req); err != nil { - h.logger.Error("SetLeagueReq failed", "error", err) - h.mongoLoggerSvc.Error("SetLeagueReq failed to parse request body", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), + domain.InternalServerErrorLogger.Error("SetLeagueReq failed to parse request body", + append(queryLogFields, zap.Error(err))..., ) 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) if !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } - h.mongoLoggerSvc.Info("Failed to validate SetLeagueActiveReq", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + domain.BadRequestLogger.Info("Failed to validate SetLeagueActiveReq", append(queryLogFields, zap.Error(err))...) return fiber.NewError(fiber.StatusBadRequest, errMsg) } - if err := h.leagueSvc.SetLeagueActive(c.Context(), int64(leagueId), req.IsActive); err != nil { - h.mongoLoggerSvc.Error("Failed to update league active", - zap.Int64("leagueID", int64(leagueId)), - zap.Bool("is_active", req.IsActive), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + if err := h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{ + LeagueID: int64(leagueId), + CompanyID: companyID.Value, + IsActive: domain.ValidBool{ + Value: req.IsActive, + Valid: true, + }, + }); 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()) } - h.mongoLoggerSvc.Info("League Active has been successfully updated", - 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()), - ) + domain.SuccessResLogger.Info("League Active has been successfully updated", queryLogFields...) return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil) } @@ -175,8 +277,13 @@ type SetLeagueAsFeatured struct { // @Success 200 {object} response.APIResponse // @Failure 400 {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 { + 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") if leagueIdStr == "" { 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") } + queryLogFields := []zap.Field{ + zap.Int64("company_id", companyID.Value), + zap.Bool("company_valid", companyID.Valid), + zap.String("league_id", leagueIdStr), + } var req SetLeagueAsFeatured if err := c.BodyParser(&req); err != nil { - h.logger.Error("SetLeagueFeaturedReq failed", "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()), - ) + domain.BadRequestLogger.Info("SetLeagueFeaturedReq failed to parse request body", append(queryLogFields, zap.Error(err))...) return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request body:"+err.Error()) } valErrs, ok := h.validator.Validate(c, req) + queryLogFields = append(queryLogFields, zap.Bool("is_featured", req.IsFeatured)) if !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } - h.mongoLoggerSvc.Info("Failed to validate SetLeagueFeaturedReq", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + domain.BadRequestLogger.Info("Failed to validate SetLeagueFeaturedReq", append(queryLogFields, zap.Error(err))...) return fiber.NewError(fiber.StatusBadRequest, errMsg) } - err = h.leagueSvc.UpdateLeague(c.Context(), domain.UpdateLeague{ - ID: int64(leagueId), + err = h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{ + LeagueID: int64(leagueId), + CompanyID: companyID.Value, IsFeatured: domain.ValidBool{ Value: req.IsFeatured, Valid: true, }, }) if err != nil { - h.mongoLoggerSvc.Error("Failed to update league", - zap.Int64("leagueID", int64(leagueId)), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) + domain.InternalServerErrorLogger.Error("Failed to update league", append(queryLogFields, zap.Error(err))...) 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)), - zap.Int64("leagueID", int64(leagueId)), - zap.Bool("is_featured", req.IsFeatured), - zap.Int("status_code", fiber.StatusOK), - zap.Time("timestamp", time.Now()), - ) + + domain.SuccessResLogger.Info("League Featured has been successfully updated", queryLogFields...) return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil) } diff --git a/internal/web_server/handlers/odd_handler.go b/internal/web_server/handlers/odd_handler.go index 3412499..3d62b35 100644 --- a/internal/web_server/handlers/odd_handler.go +++ b/internal/web_server/handlers/odd_handler.go @@ -1,35 +1,109 @@ package handlers import ( + "strconv" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" - "strconv" ) -// GetALLPrematchOdds -// @Summary Retrieve all prematch odds -// @Description Retrieve all prematch odds from the database +// 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/odds [get] -func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error { - odds, err := h.prematchSvc.GetALLPrematchOdds(c.Context()) +func (h *Handler) GetAllOdds(c *fiber.Ctx) error { + 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 { - logFields := append([]zap.Field{}, domain.InternalServerErrorZapFields...) - logFields = append(logFields, zap.Error(err)) - h.mongoLoggerSvc.Error("Failed to retrieve all prematch odds", logFields...) + 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 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 // @Description Retrieve raw odds records using a Market ID // @Tags prematch @@ -41,7 +115,7 @@ func (h *Handler) GetALLPrematchOdds(c *fiber.Ctx) error { // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @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{ zap.String("market_id", c.Params("market_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") 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") } upcomingID := c.Params("upcoming_id") 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") } - rawOdds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketID, upcomingID) + rawOdds, err := h.prematchSvc.GetOddsByMarketID(c.Context(), marketID, upcomingID) if err != nil { // Lets turn this into a warn because this is constantly going off - logFields = append(logFields, zap.Error(err)) - h.mongoLoggerSvc.Warn("Failed to get raw odds by market ID", append(logFields, domain.InternalServerErrorZapFields...)...) + domain.InternalServerErrorLogger.Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...) 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) // @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination // @Tags prematch @@ -92,31 +214,89 @@ func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error { upcomingID := c.Params("upcoming_id") 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") } limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10 if err != nil || limit <= 0 { 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") } offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0 if err != nil || offset < 0 { 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()) } - odds, err := h.prematchSvc.GetPrematchOddsByUpcomingID(c.Context(), upcomingID) + odds, err := h.prematchSvc.GetOddsByEventID(c.Context(), upcomingID, domain.OddMarketWithEventFilter{}) if err != nil { logFields = append(logFields, zap.Error(err)) - h.mongoLoggerSvc.Error("Failed to retrieve prematch odds", append(logFields, domain.InternalServerErrorZapFields...)...) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve prematch odds"+err.Error()) + 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, "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) } diff --git a/internal/web_server/handlers/user.go b/internal/web_server/handlers/user.go index 8779de9..f31452b 100644 --- a/internal/web_server/handlers/user.go +++ b/internal/web_server/handlers/user.go @@ -33,9 +33,13 @@ type CheckPhoneEmailExistRes struct { // @Success 200 {object} CheckPhoneEmailExistRes // @Failure 400 {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 { - + 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 if err := c.BodyParser(&req); err != nil { 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) } - 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 { h.mongoLoggerSvc.Error("Failed to check phone/email existence", zap.Any("request", req), @@ -87,9 +91,13 @@ type RegisterCodeReq struct { // @Success 200 {object} response.APIResponse // @Failure 400 {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 { - + 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 if err := c.BodyParser(&req); err != nil { 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") } - 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", zap.String("Medium", string(medium)), zap.String("Send To", string(sentTo)), @@ -154,9 +162,13 @@ type RegisterUserReq struct { // @Success 200 {object} response.APIResponse // @Failure 400 {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 { - + 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 if err := c.BodyParser(&req); err != nil { h.mongoLoggerSvc.Info("Failed to parse RegisterUser request", @@ -183,6 +195,8 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error { Otp: req.Otp, ReferralCode: req.ReferalCode, OtpMedium: domain.OtpMediumEmail, + CompanyID: companyID, + Role: string(domain.RoleCustomer), } medium, err := getMedium(req.Email, req.PhoneNumber) if err != nil { @@ -279,9 +293,13 @@ type ResetCodeReq struct { // @Success 200 {object} response.APIResponse // @Failure 400 {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 { - + 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 if err := c.BodyParser(&req); err != nil { 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") } - 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", zap.String("medium", string(medium)), zap.String("sentTo", string(sentTo)), @@ -349,7 +367,7 @@ type ResetPasswordReq struct { // @Success 200 {object} response.APIResponse // @Failure 400 {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 { var req ResetPasswordReq @@ -447,7 +465,7 @@ type CustomerProfileRes struct { // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @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 { userID, ok := c.Locals("user_id").(int64) @@ -499,7 +517,6 @@ func (h *Handler) CustomerProfile(c *fiber.Ctx) error { SuspendedAt: user.SuspendedAt, Suspended: user.Suspended, LastLogin: *lastLogin, - } 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 500 {object} response.APIResponse // @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 { userID, ok := c.Locals("user_id").(int64) @@ -612,7 +629,7 @@ type SearchUserByNameOrPhoneReq struct { // @Success 200 {object} UserProfileRes // @Failure 400 {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 { // TODO: Add filtering by role based on which user is calling this var req SearchUserByNameOrPhoneReq @@ -870,7 +887,7 @@ func (h *Handler) UpdateUserSuspend(c *fiber.Ctx) error { // @Success 200 {array} domain.BetRes // @Failure 400 {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 { userID, ok := c.Locals("user_id").(int64) if !ok || userID == 0 { diff --git a/internal/web_server/handlers/wallet_handler.go b/internal/web_server/handlers/wallet_handler.go index 6abd899..6b798c7 100644 --- a/internal/web_server/handlers/wallet_handler.go +++ b/internal/web_server/handlers/wallet_handler.go @@ -313,7 +313,7 @@ func (h *Handler) UpdateWalletActive(c *fiber.Ctx) error { // @Success 200 {object} CustomerWalletRes // @Failure 400 {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 { userID, ok := c.Locals("user_id").(int64) diff --git a/internal/web_server/middleware.go b/internal/web_server/middleware.go index c25584b..da941d2 100644 --- a/internal/web_server/middleware.go +++ b/internal/web_server/middleware.go @@ -67,8 +67,8 @@ func (a *App) authMiddleware(c *fiber.Ctx) error { // return fiber.NewError(fiber.StatusUnauthorized, "Refresh token missing") } - // Asserting to make sure that there is no company role without a valid company id - if claim.Role != domain.RoleSuperAdmin && claim.Role != domain.RoleCustomer && !claim.CompanyID.Valid { + // Asserting to make sure that only the super admin can have a nil company ID + if claim.Role != domain.RoleSuperAdmin && !claim.CompanyID.Valid { a.mongoLoggerSvc.Error("Company Role without Company ID", zap.Int64("userID", claim.UserId), zap.Int("status_code", fiber.StatusInternalServerError), @@ -220,3 +220,25 @@ func (a *App) WebsocketAuthMiddleware(c *fiber.Ctx) error { ) 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() +} diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 71e45c6..7b8e513 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -62,7 +62,10 @@ func (a *App) initAppRoutes() { }) }) + // Groups groupV1 := a.fiber.Group("/api/v1") + tenant := groupV1.Group("/:tenant_slug", a.TenantMiddleware) + tenantAuth := groupV1.Group("/:tenant_slug", a.authMiddleware, a.TenantMiddleware) //Direct_deposit groupV1.Post("/direct_deposit", a.authMiddleware, h.InitiateDirectDeposit) @@ -82,8 +85,9 @@ func (a *App) initAppRoutes() { }) // Auth Routes - groupV1.Post("/auth/customer-login", h.LoginCustomer) - groupV1.Post("/auth/admin-login", h.LoginAdmin) + tenant.Post("/auth/customer-login", h.LoginCustomer) + tenant.Post("/auth/admin-login", h.LoginAdmin) + groupV1.Post("/auth/super-login", h.LoginSuper) groupV1.Post("/auth/refresh", h.RefreshToken) groupV1.Post("/auth/logout", a.authMiddleware, h.LogOutCustomer) 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) // User Routes - groupV1.Post("/user/resetPassword", h.ResetPassword) - groupV1.Post("/user/sendResetCode", h.SendResetCode) - groupV1.Post("/user/register", h.RegisterUser) - groupV1.Post("/user/sendRegisterCode", h.SendRegisterCode) - groupV1.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist) - groupV1.Get("/user/customer-profile", a.authMiddleware, h.CustomerProfile) - groupV1.Get("/user/admin-profile", a.authMiddleware, h.AdminProfile) + tenant.Post("/user/resetPassword", h.ResetPassword) + tenant.Post("/user/sendResetCode", h.SendResetCode) + tenant.Post("/user/register", h.RegisterUser) + tenant.Post("/user/sendRegisterCode", h.SendRegisterCode) + tenant.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist) + tenantAuth.Get("/user/customer-profile", h.CustomerProfile) + tenantAuth.Get("/user/admin-profile", h.AdminProfile) + tenantAuth.Get("/user/bets", h.GetBetByUserID) + groupV1.Get("/user/single/:id", a.authMiddleware, h.GetUserByID) - 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.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser) - groupV1.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet) - groupV1.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone) + tenantAuth.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet) + tenantAuth.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone) // Referral Routes 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.Get("/manager/:id/branch", a.authMiddleware, h.GetBranchByManagerID) - groupV1.Get("/odds", h.GetALLPrematchOdds) - groupV1.Get("/odds/upcoming/:upcoming_id", h.GetOddsByUpcomingID) - groupV1.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetRawOddsByMarketID) + groupV1.Get("/odds", a.authMiddleware, a.SuperAdminOnly, h.GetAllOdds) + groupV1.Get("/odds/upcoming/:upcoming_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByUpcomingID) + groupV1.Get("/odds/upcoming/:upcoming_id/market/:market_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByMarketID) + + tenant.Get("/odds", h.GetAllTenantOdds) + tenant.Get("/odds/upcoming/:upcoming_id", h.GetTenantOddsByUpcomingID) + tenant.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetTenantOddsByMarketID) - groupV1.Get("/events", h.GetAllUpcomingEvents) - groupV1.Get("/events/:id", h.GetUpcomingEventByID) + 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.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 - groupV1.Get("/leagues", h.GetAllLeagues) - groupV1.Put("/leagues/:id/set-active", h.SetLeagueActive) - groupV1.Put("/leagues/:id/featured", h.SetLeagueFeatured) + tenant.Get("/leagues", h.GetAllLeagues) + tenant.Put("/leagues/:id/set-active", h.SetLeagueActive) + tenant.Put("/leagues/:id/featured", h.SetLeagueFeatured) + groupV1.Get("/leagues", a.authMiddleware, a.SuperAdminOnly, h.GetAllLeagues) groupV1.Get("/result/:id", h.GetResultsByEventID) @@ -352,5 +365,5 @@ func (a *App) initAppRoutes() { groupV1.Get("/settings", a.authMiddleware, h.GetSettingList) groupV1.Get("/settings/:key", a.authMiddleware, h.GetSettingByKey) groupV1.Put("/settings", a.authMiddleware, h.UpdateSettingList) - + }