feat: refactoring entire system to be multi-tenant

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

View File

@ -1,59 +1,207 @@
BEGIN;
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);
id,
name,
location,
wallet_id,
branch_manager_id,
company_id,
is_self_owned,
profit_percent,
is_active,
created_at,
updated_at
)
VALUES (
1,
'Test Branch',
'addis_ababa',
4,
2,
1,
TRUE,
0.10,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
COMMIT;

View File

@ -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),
@ -585,3 +652,6 @@ ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(i
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;
ALTER TABLE company_league_settings
ADD CONSTRAINT fk_league_settings_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_league_settings_league FOREIGN KEY (league_id) REFERENCES league_id(id) ON DELETE CASCADE;

View File

@ -1,12 +1,17 @@
-- name: GetUserByEmailPhone :one
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

View File

@ -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

View File

@ -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;
-- -- 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;

View File

@ -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 *

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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: GetAllLeagues :many
SELECT id,
name,
country_code,
bet365_id,
-- name: InsertLeagueSettings :exec
INSERT INTO company_league_settings (
company_id,
league_id,
is_active,
is_featured,
sport_id
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 *
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;
-- 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;

View File

@ -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 (

View File

@ -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;
DELETE FROM odds_market
Where event_id = $1;

View File

@ -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.*

View File

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

View File

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

View File

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

View File

@ -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"`
type InsertCustomOddsMarketParams struct {
OddsMarketID int64 `json:"odds_market_id"`
CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"`
OddValue float64 `json:"odd_value"`
RawOdds []byte `json:"raw_odds"`
}
func (q *Queries) InsertCustomOdd(ctx context.Context, arg InsertCustomOddParams) (CustomOdd, error) {
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

View File

@ -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"`
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,

File diff suppressed because it is too large Load Diff

View File

@ -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
}

View File

@ -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"`
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,

View File

@ -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,16 +172,14 @@ 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
`
@ -187,8 +189,8 @@ type InsertLeagueParams struct {
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"`
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"`
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"`
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"`
SportID pgtype.Int4 `json:"sport_id"`
}
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
}

View File

@ -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 {
type CompanyEventSetting struct {
ID int64 `json:"id"`
OddID int64 `json:"odd_id"`
RawOddID int64 `json:"raw_odd_id"`
CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"`
OddValue float64 `json:"odd_value"`
CreatedAt pgtype.Timestamp `json:"created_at"`
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 {
@ -259,7 +282,8 @@ type DirectDeposit struct {
type DisabledOdd struct {
ID int64 `json:"id"`
OddID int64 `json:"odd_id"`
CompanyID int64 `json:"company_id"`
OddsMarketID int64 `json:"odds_market_id"`
RawOddID int64 `json:"raw_odd_id"`
EventID string `json:"event_id"`
CreatedAt pgtype.Timestamp `json:"created_at"`
@ -267,31 +291,30 @@ type DisabledOdd struct {
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"`
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"`
IsLive bool `json:"is_live"`
Status string `json:"status"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
Source pgtype.Text `json:"source"`
IsFeatured bool `json:"is_featured"`
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"`
WinningUpperLimit int32 `json:"winning_upper_limit"`
IsActive bool `json:"is_active"`
}
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"`
@ -320,7 +406,7 @@ type FavoriteGame struct {
type Flag struct {
ID int64 `json:"id"`
BetID pgtype.Int8 `json:"bet_id"`
OddID pgtype.Int8 `json:"odd_id"`
OddsMarketID pgtype.Int8 `json:"odds_market_id"`
Reason pgtype.Text `json:"reason"`
FlaggedAt pgtype.Timestamp `json:"flagged_at"`
Resolved pgtype.Bool `json:"resolved"`
@ -329,12 +415,27 @@ type Flag struct {
type League struct {
ID int64 `json:"id"`
Name string `json:"name"`
Img pgtype.Text `json:"img"`
ImgUrl pgtype.Text `json:"img_url"`
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"`
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,29 +455,9 @@ 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"`
OddID int64 `json:"odd_id"`
OddsMarketID int64 `json:"odds_market_id"`
RawOddID int64 `json:"raw_odd_id"`
MarketID string `json:"market_id"`
EventID string `json:"event_id"`
@ -384,6 +465,53 @@ type OddHistory struct {
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 {
ID int64 `json:"id"`
SentTo string `json:"sent_to"`
@ -599,15 +727,16 @@ type SupportedOperation struct {
}
type Team struct {
ID string `json:"id"`
ID int64 `json:"id"`
TeamName string `json:"team_name"`
Country pgtype.Text `json:"country"`
Bet365ID pgtype.Int4 `json:"bet365_id"`
LogoUrl pgtype.Text `json:"logo_url"`
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"`
}

View File

@ -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,18 +163,18 @@ 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"`
OddsMarketID int64 `json:"odds_market_id"`
MarketID string `json:"market_id"`
RawOddID int64 `json:"raw_odd_id"`
EventID string `json:"event_id"`
@ -183,7 +183,7 @@ type InsertOddHistoryParams struct {
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,

View File

@ -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"`
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(
type GetOddsWithSettingsByMarketIDParams struct {
MarketID string `json:"market_id"`
EventID string `json:"event_id"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (OddsMarketWithSetting, error) {
row := q.db.QueryRow(ctx, GetOddsWithSettingsByMarketID, arg.MarketID, arg.EventID, arg.CompanyID)
var i OddsMarketWithSetting
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.Source,
&i.CompanyID,
&i.IsActive,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
&i.RawOdds,
&i.UpdatedAt,
)
return i, err
}
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'
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,
)

View File

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

View File

@ -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
)
`

View File

@ -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 {

View File

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

View File

@ -11,6 +11,7 @@ import (
type Company struct {
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),

View File

@ -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
CompanyID int64
EventID string
OddValue float64
RawOdds []json.RawMessage
}
func ConvertCreateCustomOdd(odd CreateCustomOdd) dbgen.InsertCustomOddParams {
type CustomOddFilter struct {
CompanyID ValidInt64
}
func ConvertCreateCustomOdd(odd CreateCustomOdd) (dbgen.InsertCustomOddParams, error) {
rawOddsBytes, err := json.Marshal(odd.RawOdds)
if err != nil {
return dbgen.InsertCustomOddParams{}, err
}
return dbgen.InsertCustomOddParams{
OddID: odd.OddID,
RawOddID: odd.RawOddID,
CompanyID: odd.CompanyID,
EventID: odd.EventID,
OddValue: odd.OddValue,
}
RawOdds: rawOddsBytes,
}, nil
}
func ConvertDBCustomOdd(dbCustomOdd dbgen.CustomOdd) CustomOdd {
func ConvertDBCustomOdd(dbCustomOdd dbgen.CustomOdd) (CustomOdd, error) {
var rawOdds []json.RawMessage
if err := json.Unmarshal(dbCustomOdd.RawOdds, &rawOdds); err != nil {
return CustomOdd{}, err
}
return CustomOdd{
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
}

View File

@ -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,
}
}

View File

@ -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 {
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 int32
AwayTeamID int32
HomeKitImage string
AwayKitImage string
LeagueID int32
HomeTeamID int64
AwayTeamID int64
HomeTeamImage string
AwayTeamImage string
LeagueID int64
LeagueName string
LeagueCC string
StartTime string
Score string
MatchMinute int
TimerStatus string
AddedTime int
MatchPeriod int
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
Status string
Source string
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 {
type EventSettings struct {
CompanyID int64
EventID string
FullScore string
HalfScore string
Status string
Scores map[string]map[string]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
}

View File

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

View File

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

View File

@ -1,6 +1,24 @@
package domain
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, //Womens International
10037109, //Europe Friendlies
10068837, //Euro U21
10041315, //Italian Serie A
10036538, //Spain Segunda
10047168, // US MLS
10043156, //England FA Cup
10042103, //France Cup
10041088, //Premier League 2
10084250, //Turkiye Super League
10041187, //Kenya Super League
10041391, //Netherlands Eredivisie
// Basketball
10041830, //NBA
10049984, //WNBA
10037165, //German Bundesliga
10036608, //Italian Lega 1
10040795, //EuroLeague
10041534, //Basketball Africa League
// Ice Hockey
10037477, //NHL
10037447, //AHL
10069385, //IIHF World Championship
// AMERICAN FOOTBALL
10037219, //NFL
// BASEBALL
10037485, // MLB
// VOLLEYBALL
10069666, //FIVB Nations League
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(),
}
}

View File

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

View File

@ -3,47 +3,184 @@ package domain
import (
"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
}

View File

@ -35,7 +35,7 @@ type OddHistoryFilter struct {
func ConvertCreateOddHistory(odd CreateOddHistory) dbgen.InsertOddHistoryParams {
return dbgen.InsertOddHistoryParams{
OddID: odd.OddID,
OddsMarketID: odd.OddID,
MarketID: odd.MarketID,
RawOddID: odd.RawOddID,
EventID: odd.EventID,
@ -46,7 +46,7 @@ func ConvertCreateOddHistory(odd CreateOddHistory) dbgen.InsertOddHistoryParams
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,

View File

@ -9,20 +9,20 @@ 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"`
}
@ -32,9 +32,9 @@ type CommonResultResponse struct {
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"`
League LeagueResultResponse `json:"league"`
Home TeamResultResponse `json:"home"`
Away TeamResultResponse `json:"away"`
SS string `json:"ss"`
}
@ -43,13 +43,13 @@ type FootballResultResponse struct {
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"`
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"`
@ -82,17 +82,17 @@ type BasketballResultResponse struct {
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"`
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"`
@ -129,15 +129,15 @@ type IceHockeyResultResponse struct {
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"`
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"`

View File

@ -17,17 +17,13 @@ type User struct {
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
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

View File

@ -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 {

View File

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

View File

@ -11,7 +11,7 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
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) {

View File

@ -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,
},

View File

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

View File

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

View File

@ -4,7 +4,6 @@ import (
"context"
"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,10 +147,7 @@ func (s *Store) UpdateFinalScore(ctx context.Context, eventID, fullScore string,
func (s *Store) UpdateEventStatus(ctx context.Context, eventID string, status domain.EventStatus) error {
params := dbgen.UpdateMatchResultParams{
Status: pgtype.Text{
String: string(status),
Valid: true,
},
Status: string(status),
ID: eventID,
}
@ -295,13 +160,6 @@ func (s *Store) UpdateEventStatus(ctx context.Context, eventID string, status do
}
func (s *Store) UpdateEventFeatured(ctx context.Context, eventID string, isFeatured bool) error {
return s.queries.UpdateEventFeatured(ctx, dbgen.UpdateEventFeaturedParams{
ID: eventID,
IsFeatured: isFeatured,
})
}
func (s *Store) IsEventMonitored(ctx context.Context, eventID string) (bool, error) {
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 {

View File

@ -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))
}

View File

@ -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"])
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},
if err != nil {
return err
}
err := s.queries.InsertNonLiveOdd(ctx, params)
fmt.Printf("Inserting Non Live Odd")
err = s.queries.InsertOddsMarket(ctx, params)
if err != nil {
_ = writeFailedMarketLog(m, err)
continue
}
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"
@ -81,7 +49,7 @@ func writeFailedMarketLog(m domain.Market, err error) error {
entry := struct {
Time string `json:"time"`
Error string `json:"error"`
Record domain.Market `json:"record"`
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)
if err != nil {
return domain.OddMarket{}, err
}
return converted
}(),
FetchedAt: odds.FetchedAt.Time,
ExpiresAt: odds.ExpiresAt.Time,
}, nil
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 {

View File

@ -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{
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{
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) {

View File

@ -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
}

View File

@ -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

View File

@ -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++ {

View File

@ -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

View File

@ -26,6 +26,9 @@ func (s *Service) GetAllCompanies(ctx context.Context, filter domain.CompanyFilt
func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) {
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)

View File

@ -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
}

View File

@ -32,162 +32,162 @@ func New(token string, store *repository.Store, mongoLogger *zap.Logger) Service
}
}
func (s *service) FetchLiveEvents(ctx context.Context) error {
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)
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),
eventLogger := logger.With(
zap.Int("page", page),
zap.Int("total_pages", totalPages),
zap.Error(err),
)
resp, err := http.Get(url)
if err != nil {
eventLogger.Error("Failed to fetch event data for page", zap.Error(err))
continue
}
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)
if err != nil {
s.mongoLogger.Error(
"Invalid time",
dataLogger := eventLogger.With(
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),
zap.String("leagueID", ev.League.ID),
zap.String("leagueName", ev.League.Name),
)
if err != nil {
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{
err = s.store.SaveLeague(ctx, domain.CreateLeague{
ID: leagueID,
Name: ev.League.Name,
IsActive: true,
IsFeatured: slices.Contains(domain.FeaturedLeagues, leagueID),
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{
event := domain.CreateEvent{
ID: ev.ID,
SportID: convertInt32(ev.SportID),
MatchName: "",
HomeTeam: ev.Home.Name,
AwayTeam: "", // handle nil safely
HomeTeamID: convertInt32(ev.Home.ID),
HomeTeamID: convertInt64(ev.Home.ID),
AwayTeamID: 0,
HomeKitImage: "",
AwayKitImage: "",
LeagueID: convertInt32(ev.League.ID),
HomeTeamImage: "",
AwayTeamImage: "",
LeagueID: convertInt64(ev.League.ID),
LeagueName: ev.League.Name,
LeagueCC: "",
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)
if err != nil {
s.mongoLogger.Error(
"failed to get event is_monitored",
eventLogger := s.mongoLogger.With(
zap.String("eventID", event.ID),
zap.Int32("leagueID", event.LeagueID),
zap.Int64("leagueID", event.LeagueID),
zap.String("leagueName", event.LeagueName),
zap.Int32("sport_id", event.SportID),
zap.Error(err),
)
if err != nil {
eventLogger.Error("failed to get event is_monitored", zap.Error(err))
return false, err
}
if !isEventMonitored {
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 nil
return true, 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)
}

View File

@ -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
}

View File

@ -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 {

View File

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

View File

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

View File

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

View File

@ -11,13 +11,32 @@ type Service interface {
FetchNonLiveOdds(ctx context.Context) error
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
}

View File

@ -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)
if err != nil {
s.mongoLogger.Error(
"failed to get is_monitored",
marketLogger := s.mongoLogger.With(
zap.String("market_id", market.MarketID),
zap.String("market_name", market.Name),
zap.String("market_name", market.MarketName),
zap.String("eventID", market.EventID),
zap.Error(err),
)
if err != nil {
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 {

View File

@ -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)
if err != nil {
s.mongoLogger.Error(
"Failed to get pending bet outcomes",
logger := s.mongoLogger.With(
zap.Int64("eventID", eventID),
zap.Error(err),
zap.Int64("sportID", sportID),
)
if err != nil {
logger.Error("Failed to get pending bet outcomes", zap.Error(err))
return fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err)
}
// if len(outcomes) == 0 {
// s.mongoLogger.Info(
// "No bets have been placed on event",
// zap.Int64("eventID", eventID),
// )
// }
for _, outcome := range outcomes {
if outcome.Expires.After(time.Now()) {
s.mongoLogger.Warn(
"Outcome is not expired yet",
zap.Int64("eventID", eventID),
outcomeLogger := logger.With(
zap.Int64("outcome_id", outcome.ID),
zap.Error(err),
zap.Int32("outcome_status", int32(outcome.Status)),
zap.Int64("outcome_bet_id", outcome.BetID),
zap.String("outcome_market_id", outcome.MarketName),
)
if outcome.Expires.After(time.Now()) {
outcomeLogger.Warn("Outcome is not expired yet", zap.Error(err))
return fmt.Errorf("Outcome has not expired yet")
}
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)
}

View File

@ -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

View File

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

View File

@ -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 {

View File

@ -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:"<jwt-token>"`
RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"`

View File

@ -29,30 +29,28 @@ import (
func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
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))
}
@ -180,7 +328,7 @@ type TopLeague struct {
LeagueName string `json:"league_name"`
LeagueCC string `json:"league_cc"`
LeagueSportID int32 `json:"league_sport_id"`
Events []domain.UpcomingEvent `json:"events"`
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())
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 {

View File

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

View File

@ -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()
}

View File

@ -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)
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)
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/wallet", a.authMiddleware, h.GetCustomerWallet)
groupV1.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone)
groupV1.Get("/user/single/:id", a.authMiddleware, h.GetUserByID)
groupV1.Post("/user/suspend", a.authMiddleware, h.UpdateUserSuspend)
groupV1.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser)
tenantAuth.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet)
tenantAuth.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone)
// Referral Routes
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)
groupV1.Get("/events", h.GetAllUpcomingEvents)
groupV1.Get("/events/:id", h.GetUpcomingEventByID)
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", 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)