feat: Add new stat stores and reporting functionalities for bets, branches, and wallets
- Introduced BetStatStore, BranchStatStore, and WalletStatStore interfaces for handling statistics. - Implemented repository methods for fetching and updating bet, branch, and wallet statistics. - Created reporting services for generating interval reports for bets, branches, companies, and wallets. - Enhanced CSV writing functionality to support dynamic struct to CSV conversion. - Added cron jobs for periodic updates of branch and wallet statistics. - Updated wallet handler to include transaction statistics in the response.
This commit is contained in:
parent
e5fdd33a52
commit
485cba3c9c
|
|
@ -114,7 +114,10 @@ func main() {
|
|||
messengerSvc := messenger.NewService(settingSvc, cfg)
|
||||
statSvc := stats.NewService(
|
||||
repository.NewCompanyStatStore(store),
|
||||
repository.NewBranchStatStore(store),
|
||||
repository.NewEventStatStore(store),
|
||||
repository.NewBetStatStore(store),
|
||||
repository.NewWalletStatStore(store),
|
||||
)
|
||||
|
||||
authSvc := authentication.NewService(
|
||||
|
|
|
|||
|
|
@ -75,6 +75,24 @@ CREATE TABLE IF NOT EXISTS wallets (
|
|||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT balance_positve CHECK (balance >= 0)
|
||||
);
|
||||
CREATE TABLE wallet_stats (
|
||||
wallet_id BIGINT NOT NULL,
|
||||
wallet_user_id BIGINT NOT NULL,
|
||||
wallet_user_first_name TEXT NOT NULL,
|
||||
wallet_user_last_name TEXT NOT NULL,
|
||||
wallet_type TEXT NOT NULL,
|
||||
interval_start TIMESTAMP NOT NULL,
|
||||
number_of_transactions BIGINT NOT NULL,
|
||||
total_transactions BIGINT NOT NULL,
|
||||
number_of_deposits BIGINT NOT NULL,
|
||||
total_deposits_amount BIGINT NOT NULL,
|
||||
number_of_withdraws BIGINT NOT NULL,
|
||||
total_withdraws_amount BIGINT NOT NULL,
|
||||
number_of_transfers BIGINT NOT NULL,
|
||||
total_transfers_amount BIGINT NOT NULL,
|
||||
updated_at TIMESTAMP DEFAULT now(),
|
||||
UNIQUE(wallet_id, interval_start)
|
||||
);
|
||||
CREATE TABLE refresh_tokens (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL,
|
||||
|
|
@ -298,6 +316,24 @@ CREATE TABLE IF NOT EXISTS branch_cashiers (
|
|||
UNIQUE (user_id, branch_id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS branch_locations (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
||||
CREATE TABLE branch_stats (
|
||||
branch_id BIGINT NOT NULL,
|
||||
branch_name TEXT NOT NULL,
|
||||
company_id BIGINT NOT NULL,
|
||||
company_name TEXT NOT NULL,
|
||||
company_slug TEXT NOT NULL,
|
||||
interval_start TIMESTAMP NOT NULL,
|
||||
total_bets BIGINT NOT NULL,
|
||||
total_stake BIGINT NOT NULL,
|
||||
deducted_stake BIGINT NOT NULL,
|
||||
total_cash_out BIGINT NOT NULL,
|
||||
total_cash_backs BIGINT NOT NULL,
|
||||
number_of_unsettled BIGINT NOT NULL,
|
||||
total_unsettled_amount BIGINT NOT NULL,
|
||||
total_cashiers BIGINT NOT NULL,
|
||||
updated_at TIMESTAMP DEFAULT now(),
|
||||
UNIQUE(branch_id, interval_start)
|
||||
);
|
||||
CREATE TABLE events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
source_event_id TEXT NOT NULL,
|
||||
|
|
@ -435,6 +471,8 @@ CREATE TABLE companies (
|
|||
);
|
||||
CREATE TABLE company_stats (
|
||||
company_id BIGINT NOT NULL,
|
||||
company_name TEXT NOT NULL,
|
||||
company_slug TEXT NOT NULL,
|
||||
interval_start TIMESTAMP NOT NULL,
|
||||
total_bets BIGINT NOT NULL,
|
||||
total_stake BIGINT NOT NULL,
|
||||
|
|
@ -634,11 +672,27 @@ SELECT branches.*,
|
|||
users.phone_number AS manager_phone_number,
|
||||
wallets.balance,
|
||||
wallets.is_active AS wallet_is_active,
|
||||
companies.name AS company_name
|
||||
companies.name AS company_name,
|
||||
COALESCE(bs.total_bets, 0) AS total_bets,
|
||||
COALESCE(bs.total_stake, 0) AS total_stake,
|
||||
COALESCE(bs.deducted_stake, 0) AS deducted_stake,
|
||||
COALESCE(bs.total_cash_out, 0) AS total_cash_out,
|
||||
COALESCE(bs.total_cash_backs, 0) AS total_cash_backs,
|
||||
COALESCE(bs.number_of_unsettled, 0) AS number_of_unsettled,
|
||||
COALESCE(bs.total_unsettled_amount, 0) AS total_unsettled_amount,
|
||||
COALESCE(bs.total_cashiers, 0) AS total_cashiers,
|
||||
bs.updated_at AS stats_updated_at
|
||||
FROM branches
|
||||
LEFT JOIN users ON branches.branch_manager_id = users.id
|
||||
LEFT JOIN wallets ON wallets.id = branches.wallet_id
|
||||
JOIN companies ON companies.id = branches.company_id;
|
||||
JOIN companies ON companies.id = branches.company_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT *
|
||||
FROM branch_stats s
|
||||
WHERE s.branch_id = branches.id
|
||||
ORDER BY s.interval_start DESC
|
||||
LIMIT 1
|
||||
) bs ON true;
|
||||
CREATE TABLE IF NOT EXISTS supported_operations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
|
|
@ -679,11 +733,27 @@ SELECT cw.id,
|
|||
cw.created_at,
|
||||
users.first_name,
|
||||
users.last_name,
|
||||
users.phone_number
|
||||
users.phone_number,
|
||||
COALESCE(cs.number_of_transactions, 0) AS number_of_transactions,
|
||||
COALESCE(cs.total_transactions, 0) AS total_transactions,
|
||||
COALESCE(cs.number_of_deposits, 0) AS number_of_deposits,
|
||||
COALESCE(cs.total_deposits_amount, 0) AS total_deposits_amount,
|
||||
COALESCE(cs.number_of_withdraws, 0) AS number_of_withdraws,
|
||||
COALESCE(cs.total_withdraws_amount, 0) AS total_withdraws_amount,
|
||||
COALESCE(cs.number_of_transfers, 0) AS number_of_transfers,
|
||||
COALESCE(cs.total_transfers_amount, 0) AS total_transfers_amount,
|
||||
cs.updated_at AS stats_updated_at
|
||||
FROM customer_wallets cw
|
||||
JOIN wallets rw ON cw.regular_wallet_id = rw.id
|
||||
JOIN wallets sw ON cw.static_wallet_id = sw.id
|
||||
JOIN users ON users.id = cw.customer_id;
|
||||
JOIN users ON users.id = cw.customer_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT *
|
||||
FROM wallet_stats s
|
||||
WHERE s.wallet_id = cw.regular_wallet
|
||||
ORDER BY s.interval_start DESC
|
||||
LIMIT 1
|
||||
) cs ON true;
|
||||
CREATE VIEW wallet_transfer_details AS
|
||||
SELECT wt.*,
|
||||
users.first_name,
|
||||
|
|
@ -823,7 +893,8 @@ SELECT r.*,
|
|||
c.name AS company_name,
|
||||
c.slug AS company_slug,
|
||||
u.first_name AS requester_first_name,
|
||||
u.last_name AS requester_last_name
|
||||
u.last_name AS requester_last_name,
|
||||
u.role AS requester_role
|
||||
FROM report_requests r
|
||||
LEFT JOIN companies c ON c.id = r.company_id
|
||||
LEFT JOIN users u ON u.id = r.requested_by;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,48 @@
|
|||
-- name: GetBetStatsByInterval :many
|
||||
SELECT DATE_TRUNC(sqlc.narg('interval'), created_at)::timestamp AS date,
|
||||
COUNT(*) as total_bets,
|
||||
SUM(amount) AS total_stake,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 0 THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
) as active_bets,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 1 THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
) as total_wins,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 2 THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
) as total_losses,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 1 THEN amount * total_odds
|
||||
ELSE 0
|
||||
END
|
||||
) as win_balance,
|
||||
COUNT(*) FILTER (
|
||||
WHERE status = 5
|
||||
) AS number_of_unsettled,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 5 THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
) AS total_unsettled_amount,
|
||||
COUNT(*) FILTER (
|
||||
WHERE is_shop_bet = TRUE
|
||||
) AS total_shop_bets
|
||||
FROM bets
|
||||
WHERE (
|
||||
bets.company_id = sqlc.narg('company_id')
|
||||
OR sqlc.narg('company_id') IS NULL
|
||||
);
|
||||
-- name: GetBetSummary :one
|
||||
SELECT SUM(amount) as total_stakes,
|
||||
COUNT(*) as total_bets,
|
||||
|
|
|
|||
|
|
@ -1,31 +1,128 @@
|
|||
-- name: GetBranchStats :many
|
||||
SELECT b.branch_id,
|
||||
-- name: UpdateBranchStats :exec
|
||||
WITH -- Aggregate bet data per branch
|
||||
bet_stats AS (
|
||||
SELECT branch_id,
|
||||
COUNT(*) AS total_bets,
|
||||
COALESCE(SUM(amount), 0) AS total_stake,
|
||||
COALESCE(
|
||||
SUM(amount) * MAX(companies.deducted_percentage),
|
||||
0
|
||||
) AS deducted_stake,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN cashed_out THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_cash_out,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 3 THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_cash_backs,
|
||||
COUNT(*) FILTER (
|
||||
WHERE status = 5
|
||||
) AS number_of_unsettled,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 5 THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_unsettled_amount
|
||||
FROM shop_bet_detail
|
||||
LEFT JOIN branches ON branches.id = shop_bet_detail.branch_id
|
||||
GROUP BY branch_id
|
||||
),
|
||||
cashier_stats AS (
|
||||
SELECT branch_id,
|
||||
COUNT(*) AS total_cashiers
|
||||
FROM branch_cashiers
|
||||
GROUP BY branch_id
|
||||
)
|
||||
INSERT INTO branch_stats (
|
||||
branch_id,
|
||||
branch_name,
|
||||
company_id,
|
||||
company_name,
|
||||
company_slug,
|
||||
interval_start,
|
||||
total_bets,
|
||||
total_stake,
|
||||
deducted_stake,
|
||||
total_cash_out,
|
||||
total_cash_backs,
|
||||
number_of_unsettled,
|
||||
total_unsettled_amount,
|
||||
total_cashiers,
|
||||
updated_at
|
||||
)
|
||||
SELECT br.id AS branch_id,
|
||||
br.name AS branch_name,
|
||||
br.company_id,
|
||||
COUNT(*) AS total_bets,
|
||||
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN sb.cashed_out THEN b.amount -- use cashed_out from shop_bets
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_cash_out,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN b.status = 5 THEN b.amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_cash_backs
|
||||
FROM shop_bet_detail b
|
||||
JOIN branches br ON b.branch_id = br.id
|
||||
JOIN shop_bets sb ON sb.id = b.id -- join to get cashed_out
|
||||
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
||||
GROUP BY b.branch_id,
|
||||
br.name,
|
||||
br.company_id;
|
||||
c.id AS company_id,
|
||||
c.name AS company_name,
|
||||
c.slug AS company_slug,
|
||||
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||
COALESCE(b.total_bets, 0) AS total_bets,
|
||||
COALESCE(b.total_stake, 0) AS total_stake,
|
||||
COALESCE(b.deducted_stake, 0) AS deducted_stake,
|
||||
COALESCE(b.total_cash_out, 0) AS total_cash_out,
|
||||
COALESCE(b.total_cash_backs, 0) AS total_cash_backs,
|
||||
COALESCE(b.number_of_unsettled, 0) AS number_of_unsettled,
|
||||
COALESCE(b.total_unsettled_amount, 0) AS total_unsettled_amount,
|
||||
COALESCE(bc.total_cashiers, 0) AS total_cashiers,
|
||||
NOW() AS updated_at
|
||||
FROM branches br
|
||||
LEFT JOIN companies c ON c.id = br.company_id
|
||||
LEFT JOIN bet_stats bs ON b.branch_id = br.id
|
||||
LEFT JOIN cashier_stats bc ON bc.branch_id = br.id ON CONFLICT (branch_id, interval_start) DO
|
||||
UPDATE
|
||||
SET total_bets = EXCLUDED.total_bets,
|
||||
total_stake = EXCLUDED.total_stake,
|
||||
deducted_stake = EXCLUDED.deducted_stake,
|
||||
total_cash_out = EXCLUDED.total_cash_out,
|
||||
total_cash_backs = EXCLUDED.total_cash_backs,
|
||||
number_of_unsettled = EXCLUDED.number_of_unsettled,
|
||||
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
||||
total_cashiers = EXCLUDED.total_cashiers,
|
||||
updated_at = EXCLUDED.updated_at;
|
||||
-- name: GetBranchStatsByID :many
|
||||
SELECt *
|
||||
FROM branch_stats
|
||||
WHERE branch_id = $1
|
||||
ORDER BY interval_start DESC;
|
||||
-- name: GetBranchStats :many
|
||||
SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start,
|
||||
branch_stats.branch_id,
|
||||
branch_stats.branch_name,
|
||||
branch_stats.company_id,
|
||||
branch_stats.company_name,
|
||||
branch_stats.company_slug,
|
||||
branch_stats.total_bets,
|
||||
branch_stats.total_stake,
|
||||
branch_stats.deducted_stake,
|
||||
branch_stats.total_cash_out,
|
||||
branch_stats.total_cash_backs,
|
||||
branch_stats.number_of_unsettled,
|
||||
branch_stats.total_unsettled_amount,
|
||||
branch_stats.total_cashiers,
|
||||
branch_stats.updated_at
|
||||
FROM branch_stats
|
||||
WHERE (
|
||||
branch_stats.branch_id = sqlc.narg('branch_id')
|
||||
OR sqlc.narg('branch_id') IS NULL
|
||||
)
|
||||
AND (
|
||||
branch_stats.company_id = sqlc.narg('company_id')
|
||||
OR sqlc.narg('company_id') IS NULL
|
||||
)
|
||||
GROUP BY interval_start
|
||||
ORDER BY interval_start DESC;
|
||||
|
|
@ -72,6 +72,8 @@ branch_stats AS (
|
|||
) -- Final combined aggregation
|
||||
INSERT INTO company_stats (
|
||||
company_id,
|
||||
company_name,
|
||||
company_slug,
|
||||
interval_start,
|
||||
total_bets,
|
||||
total_stake,
|
||||
|
|
@ -89,6 +91,8 @@ INSERT INTO company_stats (
|
|||
updated_at
|
||||
)
|
||||
SELECT c.id AS company_id,
|
||||
c.name AS company_name,
|
||||
c.slug AS company_slug,
|
||||
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||
COALESCE(b.total_bets, 0) AS total_bets,
|
||||
COALESCE(b.total_stake, 0) AS total_stake,
|
||||
|
|
@ -118,7 +122,7 @@ SET total_bets = EXCLUDED.total_bets,
|
|||
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
||||
total_admins = EXCLUDED.total_admins,
|
||||
total_managers = EXCLUDED.total_managers,
|
||||
total_cashiers = EXCLUDED.total_cashiers,
|
||||
SETtotal_cashiers = EXCLUDED.total_cashiers,
|
||||
total_customers = EXCLUDED.total_customers,
|
||||
total_approvers = EXCLUDED.total_approvers,
|
||||
total_branches = EXCLUDED.total_branches,
|
||||
|
|
@ -130,7 +134,23 @@ WHERE company_id = $1
|
|||
ORDER BY interval_start DESC;
|
||||
-- name: GetCompanyStats :many
|
||||
SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start,
|
||||
company_stats.*
|
||||
company_stats.company_id,
|
||||
company_stats.company_name,
|
||||
company_stats.company_slug,
|
||||
company_stats.total_bets,
|
||||
company_stats.total_stake,
|
||||
company_stats.deducted_stake,
|
||||
company_stats.total_cash_out,
|
||||
company_stats.total_cash_backs,
|
||||
company_stats.number_of_unsettled,
|
||||
company_stats.total_unsettled_amount,
|
||||
company_stats.total_admins,
|
||||
company_stats.total_managers,
|
||||
company_stats.total_cashiers,
|
||||
company_stats.total_customers,
|
||||
company_stats.total_approvers,
|
||||
company_stats.total_branches,
|
||||
company_stats.updated_at
|
||||
FROM company_stats
|
||||
WHERE (
|
||||
company_stats.company_id = sqlc.narg('company_id')
|
||||
|
|
|
|||
130
db/query/wallet_stats.sql
Normal file
130
db/query/wallet_stats.sql
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
-- name: UpdateWalletStats :exec
|
||||
WITH all_transfers AS (
|
||||
SELECT sender_wallet_id AS wallet_id,
|
||||
amount,
|
||||
type
|
||||
FROM wallet_transfer
|
||||
WHERE sender_wallet_id IS NOT NULL
|
||||
UNION ALL
|
||||
SELECT receiver_wallet_id AS wallet_id,
|
||||
amount,
|
||||
type
|
||||
FROM wallet_transfer
|
||||
WHERE receiver_wallet_id IS NOT NULL
|
||||
),
|
||||
transfer_stats AS (
|
||||
SELECT wallet_id,
|
||||
COUNT(*) AS number_of_transactions,
|
||||
COALESCE(SUM(amount), 0) AS total_transactions,
|
||||
COUNT(*) FILTER (
|
||||
WHERE type = 'deposit'
|
||||
) AS number_of_deposits,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN type = 'deposit' THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_deposits_amount,
|
||||
COUNT(*) FILTER (
|
||||
WHERE type = 'withdraw'
|
||||
) AS number_of_withdraws,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN type = 'withdraw' THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_withdraws_amount,
|
||||
COUNT(*) FILTER (
|
||||
WHERE type = 'wallet'
|
||||
) AS number_of_transfers,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN type = 'wallet' THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_transfers_amount
|
||||
FROM all_transfers
|
||||
GROUP BY wallet_id
|
||||
)
|
||||
INSERT INTO wallet_stats(
|
||||
wallet_id,
|
||||
wallet_user_id,
|
||||
wallet_user_first_name,
|
||||
wallet_user_last_name,
|
||||
wallet_type,
|
||||
interval_start,
|
||||
number_of_transactions,
|
||||
total_transactions,
|
||||
number_of_deposits,
|
||||
total_deposits_amount,
|
||||
number_of_withdraws,
|
||||
total_withdraws_amount,
|
||||
number_of_transfers,
|
||||
total_transfers_amount,
|
||||
updated_at
|
||||
)
|
||||
SELECT w.id AS wallet_id,
|
||||
w.user_id AS wallet_user_id,
|
||||
u.first_name AS wallet_user_first_name,
|
||||
u.last_name AS wallet_user_last_name,
|
||||
w.type AS wallet_type,
|
||||
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||
COALESCE(ts.number_of_transactions, 0) AS number_of_transactions,
|
||||
COALESCE(ts.total_transactions, 0) AS total_transactions,
|
||||
COALESCE(ts.number_of_deposits, 0) AS number_of_deposits,
|
||||
COALESCE(ts.total_deposits_amount, 0) AS total_deposits_amount,
|
||||
COALESCE(ts.number_of_withdraws, 0) AS number_of_withdraws,
|
||||
COALESCE(ts.total_withdraws_amount, 0) AS total_withdraws_amount,
|
||||
COALESCE(ts.number_of_transfers, 0) AS number_of_transfers,
|
||||
COALESCE(ts.total_transfers_amount, 0) AS total_transfers_amount,
|
||||
NOW() AS updated_at
|
||||
FROM wallets w
|
||||
LEFT JOIN users u ON u.id = w.user_id
|
||||
LEFT JOIN transfer_stats ts ON ts.wallet_id = w.id ON CONFLICT (wallet_id, interval_start) DO
|
||||
UPDATE
|
||||
SET number_of_transactions = EXCLUDED.number_of_transactions,
|
||||
total_transactions = EXCLUDED.total_transactions,
|
||||
number_of_deposits = EXCLUDED.number_of_deposits,
|
||||
total_deposits_amount = EXCLUDED.total_deposits_amount,
|
||||
number_of_withdraws = EXCLUDED.number_of_withdraws,
|
||||
total_withdraws_amount = EXCLUDED.total_withdraws_amount,
|
||||
number_of_transfers = EXCLUDED.number_of_transfers,
|
||||
total_transfers_amount = EXCLUDED.total_transfers_amount,
|
||||
updated_at = EXCLUDED.updated_at;
|
||||
-- name: GetWalletStatsByID :many
|
||||
SELECT *
|
||||
FROM wallet_stats
|
||||
WHERE wallet_id = $1
|
||||
ORDER BY interval_start DESC;
|
||||
-- name: GetWalletStats :many
|
||||
SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start,
|
||||
wallet_stats.wallet_id,
|
||||
wallet_stats.wallet_user_id,
|
||||
wallet_stats.wallet_user_first_name,
|
||||
wallet_stats.wallet_user_last_name,
|
||||
wallet_stats.wallet_type,
|
||||
wallet_stats.number_of_transactions,
|
||||
wallet_stats.total_transactions,
|
||||
wallet_stats.number_of_deposits,
|
||||
wallet_stats.total_deposits_amount,
|
||||
wallet_stats.number_of_withdraws,
|
||||
wallet_stats.total_withdraws_amount,
|
||||
wallet_stats.number_of_transfers,
|
||||
wallet_stats.total_transfers_amount,
|
||||
wallet_stats.updated_at
|
||||
FROM wallet_stats
|
||||
WHERE (
|
||||
wallet_stats.wallet_user_id = sqlc.narg('user_id')
|
||||
OR sqlc.narg('user_id') IS NULL
|
||||
)
|
||||
GROUP BY interval_start
|
||||
ORDER BY interval_start DESC;
|
||||
|
|
@ -116,6 +116,102 @@ func (q *Queries) GetBetStats(ctx context.Context, arg GetBetStatsParams) ([]Get
|
|||
return items, nil
|
||||
}
|
||||
|
||||
const GetBetStatsByInterval = `-- name: GetBetStatsByInterval :many
|
||||
SELECT DATE_TRUNC($1, created_at)::timestamp AS date,
|
||||
COUNT(*) as total_bets,
|
||||
SUM(amount) AS total_stake,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 0 THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
) as active_bets,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 1 THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
) as total_wins,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 2 THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
) as total_losses,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 1 THEN amount * total_odds
|
||||
ELSE 0
|
||||
END
|
||||
) as win_balance,
|
||||
COUNT(*) FILTER (
|
||||
WHERE status = 5
|
||||
) AS number_of_unsettled,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 5 THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
) AS total_unsettled_amount,
|
||||
COUNT(*) FILTER (
|
||||
WHERE is_shop_bet = TRUE
|
||||
) AS total_shop_bets
|
||||
FROM bets
|
||||
WHERE (
|
||||
bets.company_id = $2
|
||||
OR $2 IS NULL
|
||||
)
|
||||
`
|
||||
|
||||
type GetBetStatsByIntervalParams struct {
|
||||
Interval pgtype.Text `json:"interval"`
|
||||
CompanyID pgtype.Int8 `json:"company_id"`
|
||||
}
|
||||
|
||||
type GetBetStatsByIntervalRow struct {
|
||||
Date pgtype.Timestamp `json:"date"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake int64 `json:"total_stake"`
|
||||
ActiveBets int64 `json:"active_bets"`
|
||||
TotalWins int64 `json:"total_wins"`
|
||||
TotalLosses int64 `json:"total_losses"`
|
||||
WinBalance int64 `json:"win_balance"`
|
||||
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||
TotalShopBets int64 `json:"total_shop_bets"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetBetStatsByInterval(ctx context.Context, arg GetBetStatsByIntervalParams) ([]GetBetStatsByIntervalRow, error) {
|
||||
rows, err := q.db.Query(ctx, GetBetStatsByInterval, arg.Interval, arg.CompanyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetBetStatsByIntervalRow
|
||||
for rows.Next() {
|
||||
var i GetBetStatsByIntervalRow
|
||||
if err := rows.Scan(
|
||||
&i.Date,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
&i.ActiveBets,
|
||||
&i.TotalWins,
|
||||
&i.TotalLosses,
|
||||
&i.WinBalance,
|
||||
&i.NumberOfUnsettled,
|
||||
&i.TotalUnsettledAmount,
|
||||
&i.TotalShopBets,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const GetBetSummary = `-- name: GetBetSummary :one
|
||||
SELECT SUM(amount) as total_stakes,
|
||||
COUNT(*) as total_bets,
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ func (q *Queries) DeleteBranchOperation(ctx context.Context, arg DeleteBranchOpe
|
|||
}
|
||||
|
||||
const GetAllBranches = `-- name: GetAllBranches :many
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||
FROM branch_details
|
||||
WHERE (
|
||||
company_id = $1
|
||||
|
|
@ -230,6 +230,15 @@ func (q *Queries) GetAllBranches(ctx context.Context, arg GetAllBranchesParams)
|
|||
&i.Balance,
|
||||
&i.WalletIsActive,
|
||||
&i.CompanyName,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
&i.DeductedStake,
|
||||
&i.TotalCashOut,
|
||||
&i.TotalCashBacks,
|
||||
&i.NumberOfUnsettled,
|
||||
&i.TotalUnsettledAmount,
|
||||
&i.TotalCashiers,
|
||||
&i.StatsUpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -293,7 +302,7 @@ func (q *Queries) GetBranchByCashier(ctx context.Context, userID int64) (Branch,
|
|||
}
|
||||
|
||||
const GetBranchByCompanyID = `-- name: GetBranchByCompanyID :many
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||
FROM branch_details
|
||||
WHERE company_id = $1
|
||||
`
|
||||
|
|
@ -324,6 +333,15 @@ func (q *Queries) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]
|
|||
&i.Balance,
|
||||
&i.WalletIsActive,
|
||||
&i.CompanyName,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
&i.DeductedStake,
|
||||
&i.TotalCashOut,
|
||||
&i.TotalCashBacks,
|
||||
&i.NumberOfUnsettled,
|
||||
&i.TotalUnsettledAmount,
|
||||
&i.TotalCashiers,
|
||||
&i.StatsUpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -336,7 +354,7 @@ func (q *Queries) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]
|
|||
}
|
||||
|
||||
const GetBranchByID = `-- name: GetBranchByID :one
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||
FROM branch_details
|
||||
WHERE id = $1
|
||||
`
|
||||
|
|
@ -361,12 +379,21 @@ func (q *Queries) GetBranchByID(ctx context.Context, id int64) (BranchDetail, er
|
|||
&i.Balance,
|
||||
&i.WalletIsActive,
|
||||
&i.CompanyName,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
&i.DeductedStake,
|
||||
&i.TotalCashOut,
|
||||
&i.TotalCashBacks,
|
||||
&i.NumberOfUnsettled,
|
||||
&i.TotalUnsettledAmount,
|
||||
&i.TotalCashiers,
|
||||
&i.StatsUpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetBranchByManagerID = `-- name: GetBranchByManagerID :many
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||
FROM branch_details
|
||||
WHERE branch_manager_id = $1
|
||||
`
|
||||
|
|
@ -397,6 +424,15 @@ func (q *Queries) GetBranchByManagerID(ctx context.Context, branchManagerID int6
|
|||
&i.Balance,
|
||||
&i.WalletIsActive,
|
||||
&i.CompanyName,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
&i.DeductedStake,
|
||||
&i.TotalCashOut,
|
||||
&i.TotalCashBacks,
|
||||
&i.NumberOfUnsettled,
|
||||
&i.TotalUnsettledAmount,
|
||||
&i.TotalCashiers,
|
||||
&i.StatsUpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -456,7 +492,7 @@ func (q *Queries) GetBranchOperations(ctx context.Context, branchID int64) ([]Ge
|
|||
}
|
||||
|
||||
const SearchBranchByName = `-- name: SearchBranchByName :many
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||
FROM branch_details
|
||||
WHERE name ILIKE '%' || $1 || '%'
|
||||
AND (
|
||||
|
|
@ -496,6 +532,15 @@ func (q *Queries) SearchBranchByName(ctx context.Context, arg SearchBranchByName
|
|||
&i.Balance,
|
||||
&i.WalletIsActive,
|
||||
&i.CompanyName,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
&i.DeductedStake,
|
||||
&i.TotalCashOut,
|
||||
&i.TotalCashBacks,
|
||||
&i.NumberOfUnsettled,
|
||||
&i.TotalUnsettledAmount,
|
||||
&i.TotalCashiers,
|
||||
&i.StatsUpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,55 +12,60 @@ import (
|
|||
)
|
||||
|
||||
const GetBranchStats = `-- name: GetBranchStats :many
|
||||
SELECT b.branch_id,
|
||||
br.name AS branch_name,
|
||||
br.company_id,
|
||||
COUNT(*) AS total_bets,
|
||||
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN sb.cashed_out THEN b.amount -- use cashed_out from shop_bets
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_cash_out,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN b.status = 5 THEN b.amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_cash_backs
|
||||
FROM shop_bet_detail b
|
||||
JOIN branches br ON b.branch_id = br.id
|
||||
JOIN shop_bets sb ON sb.id = b.id -- join to get cashed_out
|
||||
WHERE b.created_at BETWEEN $1 AND $2
|
||||
GROUP BY b.branch_id,
|
||||
br.name,
|
||||
br.company_id
|
||||
SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start,
|
||||
branch_stats.branch_id,
|
||||
branch_stats.branch_name,
|
||||
branch_stats.company_id,
|
||||
branch_stats.company_name,
|
||||
branch_stats.company_slug,
|
||||
branch_stats.total_bets,
|
||||
branch_stats.total_stake,
|
||||
branch_stats.deducted_stake,
|
||||
branch_stats.total_cash_out,
|
||||
branch_stats.total_cash_backs,
|
||||
branch_stats.number_of_unsettled,
|
||||
branch_stats.total_unsettled_amount,
|
||||
branch_stats.total_cashiers,
|
||||
branch_stats.updated_at
|
||||
FROM branch_stats
|
||||
WHERE (
|
||||
branch_stats.branch_id = $2
|
||||
OR $2 IS NULL
|
||||
)
|
||||
AND (
|
||||
branch_stats.company_id = $3
|
||||
OR $3 IS NULL
|
||||
)
|
||||
GROUP BY interval_start
|
||||
ORDER BY interval_start DESC
|
||||
`
|
||||
|
||||
type GetBranchStatsParams struct {
|
||||
From pgtype.Timestamp `json:"from"`
|
||||
To pgtype.Timestamp `json:"to"`
|
||||
Interval pgtype.Text `json:"interval"`
|
||||
BranchID pgtype.Int8 `json:"branch_id"`
|
||||
CompanyID pgtype.Int8 `json:"company_id"`
|
||||
}
|
||||
|
||||
type GetBranchStatsRow struct {
|
||||
BranchID int64 `json:"branch_id"`
|
||||
BranchName string `json:"branch_name"`
|
||||
CompanyID int64 `json:"company_id"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalCashMade interface{} `json:"total_cash_made"`
|
||||
TotalCashOut interface{} `json:"total_cash_out"`
|
||||
TotalCashBacks interface{} `json:"total_cash_backs"`
|
||||
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||
BranchID int64 `json:"branch_id"`
|
||||
BranchName string `json:"branch_name"`
|
||||
CompanyID int64 `json:"company_id"`
|
||||
CompanyName string `json:"company_name"`
|
||||
CompanySlug string `json:"company_slug"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake int64 `json:"total_stake"`
|
||||
DeductedStake int64 `json:"deducted_stake"`
|
||||
TotalCashOut int64 `json:"total_cash_out"`
|
||||
TotalCashBacks int64 `json:"total_cash_backs"`
|
||||
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||
TotalCashiers int64 `json:"total_cashiers"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetBranchStats(ctx context.Context, arg GetBranchStatsParams) ([]GetBranchStatsRow, error) {
|
||||
rows, err := q.db.Query(ctx, GetBranchStats, arg.From, arg.To)
|
||||
rows, err := q.db.Query(ctx, GetBranchStats, arg.Interval, arg.BranchID, arg.CompanyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -69,13 +74,21 @@ func (q *Queries) GetBranchStats(ctx context.Context, arg GetBranchStatsParams)
|
|||
for rows.Next() {
|
||||
var i GetBranchStatsRow
|
||||
if err := rows.Scan(
|
||||
&i.IntervalStart,
|
||||
&i.BranchID,
|
||||
&i.BranchName,
|
||||
&i.CompanyID,
|
||||
&i.CompanyName,
|
||||
&i.CompanySlug,
|
||||
&i.TotalBets,
|
||||
&i.TotalCashMade,
|
||||
&i.TotalStake,
|
||||
&i.DeductedStake,
|
||||
&i.TotalCashOut,
|
||||
&i.TotalCashBacks,
|
||||
&i.NumberOfUnsettled,
|
||||
&i.TotalUnsettledAmount,
|
||||
&i.TotalCashiers,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -86,3 +99,149 @@ func (q *Queries) GetBranchStats(ctx context.Context, arg GetBranchStatsParams)
|
|||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const GetBranchStatsByID = `-- name: GetBranchStatsByID :many
|
||||
SELECt branch_id, branch_name, company_id, company_name, company_slug, interval_start, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, updated_at
|
||||
FROM branch_stats
|
||||
WHERE branch_id = $1
|
||||
ORDER BY interval_start DESC
|
||||
`
|
||||
|
||||
func (q *Queries) GetBranchStatsByID(ctx context.Context, branchID int64) ([]BranchStat, error) {
|
||||
rows, err := q.db.Query(ctx, GetBranchStatsByID, branchID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []BranchStat
|
||||
for rows.Next() {
|
||||
var i BranchStat
|
||||
if err := rows.Scan(
|
||||
&i.BranchID,
|
||||
&i.BranchName,
|
||||
&i.CompanyID,
|
||||
&i.CompanyName,
|
||||
&i.CompanySlug,
|
||||
&i.IntervalStart,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
&i.DeductedStake,
|
||||
&i.TotalCashOut,
|
||||
&i.TotalCashBacks,
|
||||
&i.NumberOfUnsettled,
|
||||
&i.TotalUnsettledAmount,
|
||||
&i.TotalCashiers,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const UpdateBranchStats = `-- name: UpdateBranchStats :exec
|
||||
WITH -- Aggregate bet data per branch
|
||||
bet_stats AS (
|
||||
SELECT branch_id,
|
||||
COUNT(*) AS total_bets,
|
||||
COALESCE(SUM(amount), 0) AS total_stake,
|
||||
COALESCE(
|
||||
SUM(amount) * MAX(companies.deducted_percentage),
|
||||
0
|
||||
) AS deducted_stake,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN cashed_out THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_cash_out,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 3 THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_cash_backs,
|
||||
COUNT(*) FILTER (
|
||||
WHERE status = 5
|
||||
) AS number_of_unsettled,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN status = 5 THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_unsettled_amount
|
||||
FROM shop_bet_detail
|
||||
LEFT JOIN branches ON branches.id = shop_bet_detail.branch_id
|
||||
GROUP BY branch_id
|
||||
),
|
||||
cashier_stats AS (
|
||||
SELECT branch_id,
|
||||
COUNT(*) AS total_cashiers
|
||||
FROM branch_cashiers
|
||||
GROUP BY branch_id
|
||||
)
|
||||
INSERT INTO branch_stats (
|
||||
branch_id,
|
||||
branch_name,
|
||||
company_id,
|
||||
company_name,
|
||||
company_slug,
|
||||
interval_start,
|
||||
total_bets,
|
||||
total_stake,
|
||||
deducted_stake,
|
||||
total_cash_out,
|
||||
total_cash_backs,
|
||||
number_of_unsettled,
|
||||
total_unsettled_amount,
|
||||
total_cashiers,
|
||||
updated_at
|
||||
)
|
||||
SELECT br.id AS branch_id,
|
||||
br.name AS branch_name,
|
||||
c.id AS company_id,
|
||||
c.name AS company_name,
|
||||
c.slug AS company_slug,
|
||||
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||
COALESCE(b.total_bets, 0) AS total_bets,
|
||||
COALESCE(b.total_stake, 0) AS total_stake,
|
||||
COALESCE(b.deducted_stake, 0) AS deducted_stake,
|
||||
COALESCE(b.total_cash_out, 0) AS total_cash_out,
|
||||
COALESCE(b.total_cash_backs, 0) AS total_cash_backs,
|
||||
COALESCE(b.number_of_unsettled, 0) AS number_of_unsettled,
|
||||
COALESCE(b.total_unsettled_amount, 0) AS total_unsettled_amount,
|
||||
COALESCE(bc.total_cashiers, 0) AS total_cashiers,
|
||||
NOW() AS updated_at
|
||||
FROM branches br
|
||||
LEFT JOIN companies c ON c.id = br.company_id
|
||||
LEFT JOIN bet_stats bs ON b.branch_id = br.id
|
||||
LEFT JOIN cashier_stats bc ON bc.branch_id = br.id ON CONFLICT (branch_id, interval_start) DO
|
||||
UPDATE
|
||||
SET total_bets = EXCLUDED.total_bets,
|
||||
total_stake = EXCLUDED.total_stake,
|
||||
deducted_stake = EXCLUDED.deducted_stake,
|
||||
total_cash_out = EXCLUDED.total_cash_out,
|
||||
total_cash_backs = EXCLUDED.total_cash_backs,
|
||||
number_of_unsettled = EXCLUDED.number_of_unsettled,
|
||||
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
||||
total_cashiers = EXCLUDED.total_cashiers,
|
||||
updated_at = EXCLUDED.updated_at
|
||||
`
|
||||
|
||||
func (q *Queries) UpdateBranchStats(ctx context.Context) error {
|
||||
_, err := q.db.Exec(ctx, UpdateBranchStats)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,23 @@ import (
|
|||
|
||||
const GetCompanyStats = `-- name: GetCompanyStats :many
|
||||
SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start,
|
||||
company_stats.company_id, company_stats.interval_start, company_stats.total_bets, company_stats.total_stake, company_stats.deducted_stake, company_stats.total_cash_out, company_stats.total_cash_backs, company_stats.number_of_unsettled, company_stats.total_unsettled_amount, company_stats.total_admins, company_stats.total_managers, company_stats.total_cashiers, company_stats.total_customers, company_stats.total_approvers, company_stats.total_branches, company_stats.updated_at
|
||||
company_stats.company_id,
|
||||
company_stats.company_name,
|
||||
company_stats.company_slug,
|
||||
company_stats.total_bets,
|
||||
company_stats.total_stake,
|
||||
company_stats.deducted_stake,
|
||||
company_stats.total_cash_out,
|
||||
company_stats.total_cash_backs,
|
||||
company_stats.number_of_unsettled,
|
||||
company_stats.total_unsettled_amount,
|
||||
company_stats.total_admins,
|
||||
company_stats.total_managers,
|
||||
company_stats.total_cashiers,
|
||||
company_stats.total_customers,
|
||||
company_stats.total_approvers,
|
||||
company_stats.total_branches,
|
||||
company_stats.updated_at
|
||||
FROM company_stats
|
||||
WHERE (
|
||||
company_stats.company_id = $2
|
||||
|
|
@ -31,7 +47,8 @@ type GetCompanyStatsParams struct {
|
|||
type GetCompanyStatsRow struct {
|
||||
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||
CompanyID int64 `json:"company_id"`
|
||||
IntervalStart_2 pgtype.Timestamp `json:"interval_start_2"`
|
||||
CompanyName string `json:"company_name"`
|
||||
CompanySlug string `json:"company_slug"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake int64 `json:"total_stake"`
|
||||
DeductedStake int64 `json:"deducted_stake"`
|
||||
|
|
@ -60,7 +77,8 @@ func (q *Queries) GetCompanyStats(ctx context.Context, arg GetCompanyStatsParams
|
|||
if err := rows.Scan(
|
||||
&i.IntervalStart,
|
||||
&i.CompanyID,
|
||||
&i.IntervalStart_2,
|
||||
&i.CompanyName,
|
||||
&i.CompanySlug,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
&i.DeductedStake,
|
||||
|
|
@ -87,7 +105,7 @@ func (q *Queries) GetCompanyStats(ctx context.Context, arg GetCompanyStatsParams
|
|||
}
|
||||
|
||||
const GetCompanyStatsByID = `-- name: GetCompanyStatsByID :many
|
||||
SELECT company_id, interval_start, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_admins, total_managers, total_cashiers, total_customers, total_approvers, total_branches, updated_at
|
||||
SELECT company_id, company_name, company_slug, interval_start, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_admins, total_managers, total_cashiers, total_customers, total_approvers, total_branches, updated_at
|
||||
FROM company_stats
|
||||
WHERE company_id = $1
|
||||
ORDER BY interval_start DESC
|
||||
|
|
@ -104,6 +122,8 @@ func (q *Queries) GetCompanyStatsByID(ctx context.Context, companyID int64) ([]C
|
|||
var i CompanyStat
|
||||
if err := rows.Scan(
|
||||
&i.CompanyID,
|
||||
&i.CompanyName,
|
||||
&i.CompanySlug,
|
||||
&i.IntervalStart,
|
||||
&i.TotalBets,
|
||||
&i.TotalStake,
|
||||
|
|
@ -202,6 +222,8 @@ branch_stats AS (
|
|||
) -- Final combined aggregation
|
||||
INSERT INTO company_stats (
|
||||
company_id,
|
||||
company_name,
|
||||
company_slug,
|
||||
interval_start,
|
||||
total_bets,
|
||||
total_stake,
|
||||
|
|
@ -219,6 +241,8 @@ INSERT INTO company_stats (
|
|||
updated_at
|
||||
)
|
||||
SELECT c.id AS company_id,
|
||||
c.name AS company_name,
|
||||
c.slug AS company_slug,
|
||||
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||
COALESCE(b.total_bets, 0) AS total_bets,
|
||||
COALESCE(b.total_stake, 0) AS total_stake,
|
||||
|
|
@ -248,7 +272,7 @@ SET total_bets = EXCLUDED.total_bets,
|
|||
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
||||
total_admins = EXCLUDED.total_admins,
|
||||
total_managers = EXCLUDED.total_managers,
|
||||
total_cashiers = EXCLUDED.total_cashiers,
|
||||
SETtotal_cashiers = EXCLUDED.total_cashiers,
|
||||
total_customers = EXCLUDED.total_customers,
|
||||
total_approvers = EXCLUDED.total_approvers,
|
||||
total_branches = EXCLUDED.total_branches,
|
||||
|
|
|
|||
117
gen/db/models.go
117
gen/db/models.go
|
|
@ -103,22 +103,31 @@ type BranchCashier struct {
|
|||
}
|
||||
|
||||
type BranchDetail struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Location string `json:"location"`
|
||||
ProfitPercent float32 `json:"profit_percent"`
|
||||
IsActive bool `json:"is_active"`
|
||||
WalletID int64 `json:"wallet_id"`
|
||||
BranchManagerID int64 `json:"branch_manager_id"`
|
||||
CompanyID int64 `json:"company_id"`
|
||||
IsSelfOwned bool `json:"is_self_owned"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
ManagerName interface{} `json:"manager_name"`
|
||||
ManagerPhoneNumber pgtype.Text `json:"manager_phone_number"`
|
||||
Balance pgtype.Int8 `json:"balance"`
|
||||
WalletIsActive pgtype.Bool `json:"wallet_is_active"`
|
||||
CompanyName string `json:"company_name"`
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Location string `json:"location"`
|
||||
ProfitPercent float32 `json:"profit_percent"`
|
||||
IsActive bool `json:"is_active"`
|
||||
WalletID int64 `json:"wallet_id"`
|
||||
BranchManagerID int64 `json:"branch_manager_id"`
|
||||
CompanyID int64 `json:"company_id"`
|
||||
IsSelfOwned bool `json:"is_self_owned"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
ManagerName interface{} `json:"manager_name"`
|
||||
ManagerPhoneNumber pgtype.Text `json:"manager_phone_number"`
|
||||
Balance pgtype.Int8 `json:"balance"`
|
||||
WalletIsActive pgtype.Bool `json:"wallet_is_active"`
|
||||
CompanyName string `json:"company_name"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake int64 `json:"total_stake"`
|
||||
DeductedStake int64 `json:"deducted_stake"`
|
||||
TotalCashOut int64 `json:"total_cash_out"`
|
||||
TotalCashBacks int64 `json:"total_cash_backs"`
|
||||
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||
TotalCashiers int64 `json:"total_cashiers"`
|
||||
StatsUpdatedAt pgtype.Timestamp `json:"stats_updated_at"`
|
||||
}
|
||||
|
||||
type BranchLocation struct {
|
||||
|
|
@ -134,6 +143,24 @@ type BranchOperation struct {
|
|||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
}
|
||||
|
||||
type BranchStat struct {
|
||||
BranchID int64 `json:"branch_id"`
|
||||
BranchName string `json:"branch_name"`
|
||||
CompanyID int64 `json:"company_id"`
|
||||
CompanyName string `json:"company_name"`
|
||||
CompanySlug string `json:"company_slug"`
|
||||
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake int64 `json:"total_stake"`
|
||||
DeductedStake int64 `json:"deducted_stake"`
|
||||
TotalCashOut int64 `json:"total_cash_out"`
|
||||
TotalCashBacks int64 `json:"total_cash_backs"`
|
||||
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||
TotalCashiers int64 `json:"total_cashiers"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
}
|
||||
|
||||
type CompaniesDetail struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
|
@ -222,6 +249,8 @@ type CompanySetting struct {
|
|||
|
||||
type CompanyStat struct {
|
||||
CompanyID int64 `json:"company_id"`
|
||||
CompanyName string `json:"company_name"`
|
||||
CompanySlug string `json:"company_slug"`
|
||||
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake int64 `json:"total_stake"`
|
||||
|
|
@ -249,20 +278,29 @@ type CustomerWallet struct {
|
|||
}
|
||||
|
||||
type CustomerWalletDetail struct {
|
||||
ID int64 `json:"id"`
|
||||
CustomerID int64 `json:"customer_id"`
|
||||
RegularID int64 `json:"regular_id"`
|
||||
RegularBalance int64 `json:"regular_balance"`
|
||||
StaticID int64 `json:"static_id"`
|
||||
StaticBalance int64 `json:"static_balance"`
|
||||
RegularIsActive bool `json:"regular_is_active"`
|
||||
StaticIsActive bool `json:"static_is_active"`
|
||||
RegularUpdatedAt pgtype.Timestamp `json:"regular_updated_at"`
|
||||
StaticUpdatedAt pgtype.Timestamp `json:"static_updated_at"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
ID int64 `json:"id"`
|
||||
CustomerID int64 `json:"customer_id"`
|
||||
RegularID int64 `json:"regular_id"`
|
||||
RegularBalance int64 `json:"regular_balance"`
|
||||
StaticID int64 `json:"static_id"`
|
||||
StaticBalance int64 `json:"static_balance"`
|
||||
RegularIsActive bool `json:"regular_is_active"`
|
||||
StaticIsActive bool `json:"static_is_active"`
|
||||
RegularUpdatedAt pgtype.Timestamp `json:"regular_updated_at"`
|
||||
StaticUpdatedAt pgtype.Timestamp `json:"static_updated_at"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||
TotalTransactions int64 `json:"total_transactions"`
|
||||
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||
TotalDepositsAmount int64 `json:"total_deposits_amount"`
|
||||
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||
TotalWithdrawsAmount int64 `json:"total_withdraws_amount"`
|
||||
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||
TotalTransfersAmount int64 `json:"total_transfers_amount"`
|
||||
StatsUpdatedAt pgtype.Timestamp `json:"stats_updated_at"`
|
||||
}
|
||||
|
||||
type DirectDeposit struct {
|
||||
|
|
@ -691,6 +729,7 @@ type ReportRequestDetail struct {
|
|||
CompanySlug pgtype.Text `json:"company_slug"`
|
||||
RequesterFirstName pgtype.Text `json:"requester_first_name"`
|
||||
RequesterLastName pgtype.Text `json:"requester_last_name"`
|
||||
RequesterRole pgtype.Text `json:"requester_role"`
|
||||
}
|
||||
|
||||
type ReportedIssue struct {
|
||||
|
|
@ -1038,6 +1077,24 @@ type Wallet struct {
|
|||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
}
|
||||
|
||||
type WalletStat struct {
|
||||
WalletID int64 `json:"wallet_id"`
|
||||
WalletUserID int64 `json:"wallet_user_id"`
|
||||
WalletUserFirstName string `json:"wallet_user_first_name"`
|
||||
WalletUserLastName string `json:"wallet_user_last_name"`
|
||||
WalletType string `json:"wallet_type"`
|
||||
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||
TotalTransactions int64 `json:"total_transactions"`
|
||||
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||
TotalDepositsAmount int64 `json:"total_deposits_amount"`
|
||||
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||
TotalWithdrawsAmount int64 `json:"total_withdraws_amount"`
|
||||
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||
TotalTransfersAmount int64 `json:"total_transfers_amount"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
}
|
||||
|
||||
type WalletThresholdNotification struct {
|
||||
CompanyID int64 `json:"company_id"`
|
||||
Threshold float64 `json:"threshold"`
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func (q *Queries) CreateReportRequest(ctx context.Context, arg CreateReportReque
|
|||
}
|
||||
|
||||
const GetAllReportRequests = `-- name: GetAllReportRequests :many
|
||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name
|
||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role
|
||||
FROM report_request_detail
|
||||
WHERE (
|
||||
company_id = $1
|
||||
|
|
@ -115,6 +115,7 @@ func (q *Queries) GetAllReportRequests(ctx context.Context, arg GetAllReportRequ
|
|||
&i.CompanySlug,
|
||||
&i.RequesterFirstName,
|
||||
&i.RequesterLastName,
|
||||
&i.RequesterRole,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -127,7 +128,7 @@ func (q *Queries) GetAllReportRequests(ctx context.Context, arg GetAllReportRequ
|
|||
}
|
||||
|
||||
const GetReportRequestByID = `-- name: GetReportRequestByID :one
|
||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name
|
||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role
|
||||
FROM report_request_detail
|
||||
WHERE id = $1
|
||||
`
|
||||
|
|
@ -150,12 +151,13 @@ func (q *Queries) GetReportRequestByID(ctx context.Context, id int64) (ReportReq
|
|||
&i.CompanySlug,
|
||||
&i.RequesterFirstName,
|
||||
&i.RequesterLastName,
|
||||
&i.RequesterRole,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetReportRequestByRequestedByID = `-- name: GetReportRequestByRequestedByID :many
|
||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name
|
||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role
|
||||
FROM report_request_detail
|
||||
WHERE requested_by = $1
|
||||
AND (
|
||||
|
|
@ -208,6 +210,7 @@ func (q *Queries) GetReportRequestByRequestedByID(ctx context.Context, arg GetRe
|
|||
&i.CompanySlug,
|
||||
&i.RequesterFirstName,
|
||||
&i.RequesterLastName,
|
||||
&i.RequesterRole,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ func (q *Queries) GetAllBranchWallets(ctx context.Context) ([]GetAllBranchWallet
|
|||
}
|
||||
|
||||
const GetAllCustomerWallet = `-- name: GetAllCustomerWallet :many
|
||||
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number
|
||||
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, stats_updated_at
|
||||
FROM customer_wallet_details
|
||||
`
|
||||
|
||||
|
|
@ -174,6 +174,15 @@ func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDet
|
|||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.PhoneNumber,
|
||||
&i.NumberOfTransactions,
|
||||
&i.TotalTransactions,
|
||||
&i.NumberOfDeposits,
|
||||
&i.TotalDepositsAmount,
|
||||
&i.NumberOfWithdraws,
|
||||
&i.TotalWithdrawsAmount,
|
||||
&i.NumberOfTransfers,
|
||||
&i.TotalTransfersAmount,
|
||||
&i.StatsUpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -287,7 +296,7 @@ func (q *Queries) GetCompanyByWalletID(ctx context.Context, walletID int64) (Get
|
|||
}
|
||||
|
||||
const GetCustomerWallet = `-- name: GetCustomerWallet :one
|
||||
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number
|
||||
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, stats_updated_at
|
||||
FROM customer_wallet_details
|
||||
WHERE customer_id = $1
|
||||
`
|
||||
|
|
@ -310,6 +319,15 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (Cust
|
|||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.PhoneNumber,
|
||||
&i.NumberOfTransactions,
|
||||
&i.TotalTransactions,
|
||||
&i.NumberOfDeposits,
|
||||
&i.TotalDepositsAmount,
|
||||
&i.NumberOfWithdraws,
|
||||
&i.TotalWithdrawsAmount,
|
||||
&i.NumberOfTransfers,
|
||||
&i.TotalTransfersAmount,
|
||||
&i.StatsUpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
|
|||
248
gen/db/wallet_stats.sql.go
Normal file
248
gen/db/wallet_stats.sql.go
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// source: wallet_stats.sql
|
||||
|
||||
package dbgen
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const GetWalletStats = `-- name: GetWalletStats :many
|
||||
SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start,
|
||||
wallet_stats.wallet_id,
|
||||
wallet_stats.wallet_user_id,
|
||||
wallet_stats.wallet_user_first_name,
|
||||
wallet_stats.wallet_user_last_name,
|
||||
wallet_stats.wallet_type,
|
||||
wallet_stats.number_of_transactions,
|
||||
wallet_stats.total_transactions,
|
||||
wallet_stats.number_of_deposits,
|
||||
wallet_stats.total_deposits_amount,
|
||||
wallet_stats.number_of_withdraws,
|
||||
wallet_stats.total_withdraws_amount,
|
||||
wallet_stats.number_of_transfers,
|
||||
wallet_stats.total_transfers_amount,
|
||||
wallet_stats.updated_at
|
||||
FROM wallet_stats
|
||||
WHERE (
|
||||
wallet_stats.wallet_user_id = $2
|
||||
OR $2 IS NULL
|
||||
)
|
||||
GROUP BY interval_start
|
||||
ORDER BY interval_start DESC
|
||||
`
|
||||
|
||||
type GetWalletStatsParams struct {
|
||||
Interval pgtype.Text `json:"interval"`
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
}
|
||||
|
||||
type GetWalletStatsRow struct {
|
||||
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||
WalletID int64 `json:"wallet_id"`
|
||||
WalletUserID int64 `json:"wallet_user_id"`
|
||||
WalletUserFirstName string `json:"wallet_user_first_name"`
|
||||
WalletUserLastName string `json:"wallet_user_last_name"`
|
||||
WalletType string `json:"wallet_type"`
|
||||
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||
TotalTransactions int64 `json:"total_transactions"`
|
||||
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||
TotalDepositsAmount int64 `json:"total_deposits_amount"`
|
||||
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||
TotalWithdrawsAmount int64 `json:"total_withdraws_amount"`
|
||||
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||
TotalTransfersAmount int64 `json:"total_transfers_amount"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetWalletStats(ctx context.Context, arg GetWalletStatsParams) ([]GetWalletStatsRow, error) {
|
||||
rows, err := q.db.Query(ctx, GetWalletStats, arg.Interval, arg.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetWalletStatsRow
|
||||
for rows.Next() {
|
||||
var i GetWalletStatsRow
|
||||
if err := rows.Scan(
|
||||
&i.IntervalStart,
|
||||
&i.WalletID,
|
||||
&i.WalletUserID,
|
||||
&i.WalletUserFirstName,
|
||||
&i.WalletUserLastName,
|
||||
&i.WalletType,
|
||||
&i.NumberOfTransactions,
|
||||
&i.TotalTransactions,
|
||||
&i.NumberOfDeposits,
|
||||
&i.TotalDepositsAmount,
|
||||
&i.NumberOfWithdraws,
|
||||
&i.TotalWithdrawsAmount,
|
||||
&i.NumberOfTransfers,
|
||||
&i.TotalTransfersAmount,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const GetWalletStatsByID = `-- name: GetWalletStatsByID :many
|
||||
SELECT wallet_id, wallet_user_id, wallet_user_first_name, wallet_user_last_name, wallet_type, interval_start, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, updated_at
|
||||
FROM wallet_stats
|
||||
WHERE wallet_id = $1
|
||||
ORDER BY interval_start DESC
|
||||
`
|
||||
|
||||
func (q *Queries) GetWalletStatsByID(ctx context.Context, walletID int64) ([]WalletStat, error) {
|
||||
rows, err := q.db.Query(ctx, GetWalletStatsByID, walletID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WalletStat
|
||||
for rows.Next() {
|
||||
var i WalletStat
|
||||
if err := rows.Scan(
|
||||
&i.WalletID,
|
||||
&i.WalletUserID,
|
||||
&i.WalletUserFirstName,
|
||||
&i.WalletUserLastName,
|
||||
&i.WalletType,
|
||||
&i.IntervalStart,
|
||||
&i.NumberOfTransactions,
|
||||
&i.TotalTransactions,
|
||||
&i.NumberOfDeposits,
|
||||
&i.TotalDepositsAmount,
|
||||
&i.NumberOfWithdraws,
|
||||
&i.TotalWithdrawsAmount,
|
||||
&i.NumberOfTransfers,
|
||||
&i.TotalTransfersAmount,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const UpdateWalletStats = `-- name: UpdateWalletStats :exec
|
||||
WITH all_transfers AS (
|
||||
SELECT sender_wallet_id AS wallet_id,
|
||||
amount,
|
||||
type
|
||||
FROM wallet_transfer
|
||||
WHERE sender_wallet_id IS NOT NULL
|
||||
UNION ALL
|
||||
SELECT receiver_wallet_id AS wallet_id,
|
||||
amount,
|
||||
type
|
||||
FROM wallet_transfer
|
||||
WHERE receiver_wallet_id IS NOT NULL
|
||||
),
|
||||
transfer_stats AS (
|
||||
SELECT wallet_id,
|
||||
COUNT(*) AS number_of_transactions,
|
||||
COALESCE(SUM(amount), 0) AS total_transactions,
|
||||
COUNT(*) FILTER (
|
||||
WHERE type = 'deposit'
|
||||
) AS number_of_deposits,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN type = 'deposit' THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_deposits_amount,
|
||||
COUNT(*) FILTER (
|
||||
WHERE type = 'withdraw'
|
||||
) AS number_of_withdraws,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN type = 'withdraw' THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_withdraws_amount,
|
||||
COUNT(*) FILTER (
|
||||
WHERE type = 'wallet'
|
||||
) AS number_of_transfers,
|
||||
COALESCE(
|
||||
SUM(
|
||||
CASE
|
||||
WHEN type = 'wallet' THEN amount
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) AS total_transfers_amount
|
||||
FROM all_transfers
|
||||
GROUP BY wallet_id
|
||||
)
|
||||
INSERT INTO wallet_stats(
|
||||
wallet_id,
|
||||
wallet_user_id,
|
||||
wallet_user_first_name,
|
||||
wallet_user_last_name,
|
||||
wallet_type,
|
||||
interval_start,
|
||||
number_of_transactions,
|
||||
total_transactions,
|
||||
number_of_deposits,
|
||||
total_deposits_amount,
|
||||
number_of_withdraws,
|
||||
total_withdraws_amount,
|
||||
number_of_transfers,
|
||||
total_transfers_amount,
|
||||
updated_at
|
||||
)
|
||||
SELECT w.id AS wallet_id,
|
||||
w.user_id AS wallet_user_id,
|
||||
u.first_name AS wallet_user_first_name,
|
||||
u.last_name AS wallet_user_last_name,
|
||||
w.type AS wallet_type,
|
||||
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||
COALESCE(ts.number_of_transactions, 0) AS number_of_transactions,
|
||||
COALESCE(ts.total_transactions, 0) AS total_transactions,
|
||||
COALESCE(ts.number_of_deposits, 0) AS number_of_deposits,
|
||||
COALESCE(ts.total_deposits_amount, 0) AS total_deposits_amount,
|
||||
COALESCE(ts.number_of_withdraws, 0) AS number_of_withdraws,
|
||||
COALESCE(ts.total_withdraws_amount, 0) AS total_withdraws_amount,
|
||||
COALESCE(ts.number_of_transfers, 0) AS number_of_transfers,
|
||||
COALESCE(ts.total_transfers_amount, 0) AS total_transfers_amount,
|
||||
NOW() AS updated_at
|
||||
FROM wallets w
|
||||
LEFT JOIN users u ON u.id = w.user_id
|
||||
LEFT JOIN transfer_stats ts ON ts.wallet_id = w.id ON CONFLICT (wallet_id, interval_start) DO
|
||||
UPDATE
|
||||
SET number_of_transactions = EXCLUDED.number_of_transactions,
|
||||
total_transactions = EXCLUDED.total_transactions,
|
||||
number_of_deposits = EXCLUDED.number_of_deposits,
|
||||
total_deposits_amount = EXCLUDED.total_deposits_amount,
|
||||
number_of_withdraws = EXCLUDED.number_of_withdraws,
|
||||
total_withdraws_amount = EXCLUDED.total_withdraws_amount,
|
||||
number_of_transfers = EXCLUDED.number_of_transfers,
|
||||
total_transfers_amount = EXCLUDED.total_transfers_amount,
|
||||
updated_at = EXCLUDED.updated_at
|
||||
`
|
||||
|
||||
func (q *Queries) UpdateWalletStats(ctx context.Context) error {
|
||||
_, err := q.db.Exec(ctx, UpdateWalletStats)
|
||||
return err
|
||||
}
|
||||
49
internal/domain/bet_stats.go
Normal file
49
internal/domain/bet_stats.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
)
|
||||
|
||||
type BetStatsByInterval struct {
|
||||
Date time.Time `json:"date"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake int64 `json:"total_stake"`
|
||||
ActiveBets int64 `json:"active_bets"`
|
||||
TotalWins int64 `json:"total_wins"`
|
||||
TotalLosses int64 `json:"total_losses"`
|
||||
WinBalance int64 `json:"win_balance"`
|
||||
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||
TotalShopBets int64 `json:"total_shop_bets"`
|
||||
}
|
||||
|
||||
type BetStatsByIntervalFilter struct {
|
||||
Interval ValidDateInterval
|
||||
CompanyID ValidInt64
|
||||
|
||||
}
|
||||
|
||||
func ConvertDBBetStatsByInterval(stats dbgen.GetBetStatsByIntervalRow) BetStatsByInterval {
|
||||
return BetStatsByInterval{
|
||||
Date: stats.Date.Time,
|
||||
TotalBets: stats.TotalBets,
|
||||
TotalStake: stats.TotalStake,
|
||||
ActiveBets: stats.ActiveBets,
|
||||
TotalWins: stats.TotalWins,
|
||||
TotalLosses: stats.TotalLosses,
|
||||
WinBalance: stats.WinBalance,
|
||||
NumberOfUnsettled: stats.NumberOfUnsettled,
|
||||
TotalUnsettledAmount: stats.TotalUnsettledAmount,
|
||||
TotalShopBets: stats.TotalShopBets,
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertDBBetStatsByIntervalList(stats []dbgen.GetBetStatsByIntervalRow) []BetStatsByInterval {
|
||||
result := make([]BetStatsByInterval, len(stats))
|
||||
for i, e := range stats {
|
||||
result[i] = ConvertDBBetStatsByInterval(e)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
|
@ -32,19 +34,29 @@ type BranchFilter struct {
|
|||
}
|
||||
|
||||
type BranchDetail struct {
|
||||
ID int64
|
||||
Name string
|
||||
Location string
|
||||
WalletID int64
|
||||
Balance Currency
|
||||
BranchManagerID int64
|
||||
CompanyID int64
|
||||
IsActive bool
|
||||
IsSelfOwned bool
|
||||
ManagerName string
|
||||
ManagerPhoneNumber string
|
||||
WalletIsActive bool
|
||||
ProfitPercentage float32
|
||||
ID int64
|
||||
Name string
|
||||
Location string
|
||||
WalletID int64
|
||||
Balance Currency
|
||||
BranchManagerID int64
|
||||
CompanyID int64
|
||||
IsActive bool
|
||||
IsSelfOwned bool
|
||||
ManagerName string
|
||||
ManagerPhoneNumber string
|
||||
WalletIsActive bool
|
||||
ProfitPercentage float32
|
||||
CompanyName string
|
||||
TotalBets int64
|
||||
TotalStake Currency
|
||||
DeductedStake Currency
|
||||
TotalCashOut Currency
|
||||
TotalCashBacks Currency
|
||||
NumberOfUnsettled int64
|
||||
TotalUnsettledAmount Currency
|
||||
TotalCashiers int64
|
||||
StatsUpdatedAt time.Time
|
||||
}
|
||||
|
||||
type SupportedOperation struct {
|
||||
|
|
@ -143,19 +155,29 @@ type BranchRes struct {
|
|||
}
|
||||
|
||||
type BranchDetailRes struct {
|
||||
ID int64 `json:"id" example:"1"`
|
||||
Name string `json:"name" example:"4-kilo Branch"`
|
||||
Location string `json:"location" example:"Addis Ababa"`
|
||||
WalletID int64 `json:"wallet_id" example:"1"`
|
||||
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
|
||||
CompanyID int64 `json:"company_id" example:"1"`
|
||||
IsSelfOwned bool `json:"is_self_owned" example:"false"`
|
||||
ManagerName string `json:"manager_name" example:"John Smith"`
|
||||
ManagerPhoneNumber string `json:"manager_phone_number" example:"0911111111"`
|
||||
Balance float32 `json:"balance" example:"100.5"`
|
||||
IsActive bool `json:"is_active" example:"false"`
|
||||
WalletIsActive bool `json:"is_wallet_active" example:"false"`
|
||||
ProfitPercentage float32 `json:"profit_percentage" example:"0.1"`
|
||||
ID int64 `json:"id" example:"1"`
|
||||
Name string `json:"name" example:"4-kilo Branch"`
|
||||
Location string `json:"location" example:"Addis Ababa"`
|
||||
WalletID int64 `json:"wallet_id" example:"1"`
|
||||
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
|
||||
CompanyID int64 `json:"company_id" example:"1"`
|
||||
IsSelfOwned bool `json:"is_self_owned" example:"false"`
|
||||
ManagerName string `json:"manager_name" example:"John Smith"`
|
||||
ManagerPhoneNumber string `json:"manager_phone_number" example:"0911111111"`
|
||||
Balance float32 `json:"balance" example:"100.5"`
|
||||
IsActive bool `json:"is_active" example:"false"`
|
||||
WalletIsActive bool `json:"is_wallet_active" example:"false"`
|
||||
ProfitPercentage float32 `json:"profit_percentage" example:"0.1"`
|
||||
CompanyName string `json:"company_name" example:"fortune"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake float32 `json:"total_stake"`
|
||||
DeductedStake float32 `json:"deducted_stake"`
|
||||
TotalCashOut float32 `json:"total_cash_out"`
|
||||
TotalCashBacks float32 `json:"total_cash_backs"`
|
||||
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||
TotalUnsettledAmount float32 `json:"total_unsettled_amount"`
|
||||
TotalCashiers int64 `json:"total_cashiers"`
|
||||
StatsUpdatedAt time.Time `json:"stats_updated_at"`
|
||||
}
|
||||
|
||||
func ConvertBranch(branch Branch) BranchRes {
|
||||
|
|
@ -174,19 +196,29 @@ func ConvertBranch(branch Branch) BranchRes {
|
|||
|
||||
func ConvertBranchDetail(branch BranchDetail) BranchDetailRes {
|
||||
return BranchDetailRes{
|
||||
ID: branch.ID,
|
||||
Name: branch.Name,
|
||||
Location: branch.Location,
|
||||
WalletID: branch.WalletID,
|
||||
BranchManagerID: branch.BranchManagerID,
|
||||
CompanyID: branch.CompanyID,
|
||||
IsSelfOwned: branch.IsSelfOwned,
|
||||
ManagerName: branch.ManagerName,
|
||||
ManagerPhoneNumber: branch.ManagerPhoneNumber,
|
||||
Balance: branch.Balance.Float32(),
|
||||
IsActive: branch.IsActive,
|
||||
WalletIsActive: branch.WalletIsActive,
|
||||
ProfitPercentage: branch.ProfitPercentage,
|
||||
ID: branch.ID,
|
||||
Name: branch.Name,
|
||||
Location: branch.Location,
|
||||
WalletID: branch.WalletID,
|
||||
BranchManagerID: branch.BranchManagerID,
|
||||
CompanyID: branch.CompanyID,
|
||||
IsSelfOwned: branch.IsSelfOwned,
|
||||
ManagerName: branch.ManagerName,
|
||||
ManagerPhoneNumber: branch.ManagerPhoneNumber,
|
||||
Balance: branch.Balance.Float32(),
|
||||
IsActive: branch.IsActive,
|
||||
WalletIsActive: branch.WalletIsActive,
|
||||
ProfitPercentage: branch.ProfitPercentage,
|
||||
CompanyName: branch.CompanyName,
|
||||
TotalBets: branch.TotalBets,
|
||||
TotalStake: branch.TotalStake.Float32(),
|
||||
DeductedStake: branch.DeductedStake.Float32(),
|
||||
TotalCashOut: branch.TotalCashOut.Float32(),
|
||||
TotalCashBacks: branch.TotalCashBacks.Float32(),
|
||||
NumberOfUnsettled: branch.NumberOfUnsettled,
|
||||
TotalUnsettledAmount: branch.TotalUnsettledAmount.Float32(),
|
||||
TotalCashiers: branch.TotalCashiers,
|
||||
StatsUpdatedAt: branch.StatsUpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,19 +236,29 @@ func ConvertCreateBranch(branch CreateBranch) dbgen.CreateBranchParams {
|
|||
|
||||
func ConvertDBBranchDetail(dbBranch dbgen.BranchDetail) BranchDetail {
|
||||
return BranchDetail{
|
||||
ID: dbBranch.ID,
|
||||
Name: dbBranch.Name,
|
||||
Location: dbBranch.Location,
|
||||
WalletID: dbBranch.WalletID,
|
||||
BranchManagerID: dbBranch.BranchManagerID,
|
||||
CompanyID: dbBranch.CompanyID,
|
||||
IsSelfOwned: dbBranch.IsSelfOwned,
|
||||
ManagerName: dbBranch.ManagerName.(string),
|
||||
ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String,
|
||||
Balance: Currency(dbBranch.Balance.Int64),
|
||||
IsActive: dbBranch.IsActive,
|
||||
WalletIsActive: dbBranch.WalletIsActive.Bool,
|
||||
ProfitPercentage: dbBranch.ProfitPercent,
|
||||
ID: dbBranch.ID,
|
||||
Name: dbBranch.Name,
|
||||
Location: dbBranch.Location,
|
||||
WalletID: dbBranch.WalletID,
|
||||
BranchManagerID: dbBranch.BranchManagerID,
|
||||
CompanyID: dbBranch.CompanyID,
|
||||
IsSelfOwned: dbBranch.IsSelfOwned,
|
||||
ManagerName: dbBranch.ManagerName.(string),
|
||||
ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String,
|
||||
Balance: Currency(dbBranch.Balance.Int64),
|
||||
IsActive: dbBranch.IsActive,
|
||||
WalletIsActive: dbBranch.WalletIsActive.Bool,
|
||||
ProfitPercentage: dbBranch.ProfitPercent,
|
||||
CompanyName: dbBranch.CompanyName,
|
||||
TotalBets: dbBranch.TotalBets,
|
||||
TotalStake: Currency(dbBranch.TotalStake),
|
||||
DeductedStake: Currency(dbBranch.DeductedStake),
|
||||
TotalCashOut: Currency(dbBranch.TotalCashOut),
|
||||
TotalCashBacks: Currency(dbBranch.TotalCashBacks),
|
||||
NumberOfUnsettled: dbBranch.NumberOfUnsettled,
|
||||
TotalUnsettledAmount: Currency(dbBranch.TotalUnsettledAmount),
|
||||
TotalCashiers: dbBranch.TotalCashiers,
|
||||
StatsUpdatedAt: dbBranch.StatsUpdatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,88 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
)
|
||||
|
||||
// Branch-level aggregated report
|
||||
type BranchStats struct {
|
||||
BranchID int64
|
||||
CompanyID int64
|
||||
TotalBets int64
|
||||
TotalCashIn Currency
|
||||
TotalCashOut Currency
|
||||
TotalCashBacks Currency
|
||||
type BranchStat struct {
|
||||
IntervalStart time.Time
|
||||
BranchID int64
|
||||
BranchName string
|
||||
CompanyID int64
|
||||
CompanyName string
|
||||
CompanySlug string
|
||||
TotalBets int64
|
||||
TotalStake Currency
|
||||
DeductedStake Currency
|
||||
TotalCashOut Currency
|
||||
TotalCashBacks Currency
|
||||
NumberOfUnsettled int64
|
||||
TotalUnsettledAmount Currency
|
||||
TotalCashiers int64
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type BranchStatFilter struct {
|
||||
Interval ValidDateInterval
|
||||
BranchID ValidInt64
|
||||
CompanyID ValidInt64
|
||||
}
|
||||
|
||||
func ConvertDBBranchStats(branch dbgen.BranchStat) BranchStat {
|
||||
return BranchStat{
|
||||
IntervalStart: branch.IntervalStart.Time,
|
||||
BranchID: branch.BranchID,
|
||||
BranchName: branch.BranchName,
|
||||
CompanyID: branch.CompanyID,
|
||||
CompanyName: branch.CompanyName,
|
||||
CompanySlug: branch.CompanySlug,
|
||||
TotalBets: branch.TotalBets,
|
||||
TotalStake: Currency(branch.TotalStake),
|
||||
DeductedStake: Currency(branch.DeductedStake),
|
||||
TotalCashOut: Currency(branch.TotalCashOut),
|
||||
TotalCashBacks: Currency(branch.TotalCashBacks),
|
||||
NumberOfUnsettled: branch.NumberOfUnsettled,
|
||||
TotalUnsettledAmount: Currency(branch.TotalUnsettledAmount),
|
||||
TotalCashiers: branch.TotalCashiers,
|
||||
UpdatedAt: branch.UpdatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertDBBranchStatsList(stats []dbgen.BranchStat) []BranchStat {
|
||||
result := make([]BranchStat, len(stats))
|
||||
for i, stat := range stats {
|
||||
result[i] = ConvertDBBranchStats(stat)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ConvertDBBranchStatsByInterval(branch dbgen.GetBranchStatsRow) BranchStat {
|
||||
return BranchStat{
|
||||
IntervalStart: branch.IntervalStart.Time,
|
||||
BranchID: branch.BranchID,
|
||||
BranchName: branch.BranchName,
|
||||
CompanyID: branch.CompanyID,
|
||||
CompanyName: branch.CompanyName,
|
||||
CompanySlug: branch.CompanySlug,
|
||||
TotalBets: branch.TotalBets,
|
||||
TotalStake: Currency(branch.TotalStake),
|
||||
DeductedStake: Currency(branch.DeductedStake),
|
||||
TotalCashOut: Currency(branch.TotalCashOut),
|
||||
TotalCashBacks: Currency(branch.TotalCashBacks),
|
||||
NumberOfUnsettled: branch.NumberOfUnsettled,
|
||||
TotalUnsettledAmount: Currency(branch.TotalUnsettledAmount),
|
||||
TotalCashiers: branch.TotalCashiers,
|
||||
UpdatedAt: branch.UpdatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertDBBranchStatsByIntervalList(stats []dbgen.GetBranchStatsRow) []BranchStat {
|
||||
result := make([]BranchStat, len(stats))
|
||||
for i, stat := range stats {
|
||||
result[i] = ConvertDBBranchStatsByInterval(stat)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ func ConvertGetCompany(company GetCompany) GetCompanyRes {
|
|||
AdminPhoneNumber: company.AdminPhoneNumber,
|
||||
TotalBets: company.TotalBets,
|
||||
TotalStake: company.TotalStake.Float32(),
|
||||
DeductedStake: company.DeductedPercentage,
|
||||
DeductedStake: company.DeductedStake.Float32(),
|
||||
TotalCashOut: company.TotalCashOut.Float32(),
|
||||
TotalCashBacks: company.TotalCashBacks.Float32(),
|
||||
NumberOfUnsettled: company.NumberOfUnsettled,
|
||||
|
|
@ -202,7 +202,7 @@ func ConvertDBCompanyDetails(dbCompany dbgen.CompaniesDetail) GetCompany {
|
|||
IsActive: dbCompany.IsActive,
|
||||
TotalBets: dbCompany.TotalBets,
|
||||
TotalStake: Currency(dbCompany.TotalStake),
|
||||
DeductedStake: Currency(dbCompany.DeductedPercentage),
|
||||
DeductedStake: Currency(dbCompany.DeductedStake),
|
||||
TotalCashOut: Currency(dbCompany.TotalCashOut),
|
||||
TotalCashBacks: Currency(dbCompany.TotalCashBacks),
|
||||
NumberOfUnsettled: dbCompany.NumberOfUnsettled,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import (
|
|||
)
|
||||
|
||||
type CompanyStat struct {
|
||||
CompanyID int64
|
||||
IntervalStart time.Time
|
||||
CompanyID int64
|
||||
CompanyName string
|
||||
CompanySlug string
|
||||
TotalBets int64
|
||||
TotalStake Currency
|
||||
DeductedStake Currency
|
||||
|
|
@ -24,8 +26,10 @@ type CompanyStat struct {
|
|||
UpdatedAt time.Time
|
||||
}
|
||||
type CompanyStatRes struct {
|
||||
CompanyID int64 `json:"company_id"`
|
||||
IntervalStart time.Time `json:"interval_start"`
|
||||
CompanyID int64 `json:"company_id"`
|
||||
CompanyName string `json:"company_name"`
|
||||
CompanySlug string `json:"company_slug"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
TotalStake float32 `json:"total_stake"`
|
||||
DeductedStake float32 `json:"deducted_stake"`
|
||||
|
|
@ -49,7 +53,10 @@ type CompanyStatFilter struct {
|
|||
|
||||
func ConvertDBCompanyStats(company dbgen.CompanyStat) CompanyStat {
|
||||
return CompanyStat{
|
||||
IntervalStart: company.IntervalStart.Time,
|
||||
CompanyID: company.CompanyID,
|
||||
CompanyName: company.CompanyName,
|
||||
CompanySlug: company.CompanySlug,
|
||||
TotalBets: company.TotalBets,
|
||||
TotalStake: Currency(company.TotalStake),
|
||||
DeductedStake: Currency(company.DeductedStake),
|
||||
|
|
@ -77,8 +84,10 @@ func ConvertDBCompanyStatsList(stats []dbgen.CompanyStat) []CompanyStat {
|
|||
|
||||
func ConvertDBCompanyStatsByInterval(company dbgen.GetCompanyStatsRow) CompanyStat {
|
||||
return CompanyStat{
|
||||
CompanyID: company.CompanyID,
|
||||
IntervalStart: company.IntervalStart.Time,
|
||||
CompanyID: company.CompanyID,
|
||||
CompanyName: company.CompanyName,
|
||||
CompanySlug: company.CompanySlug,
|
||||
TotalBets: company.TotalBets,
|
||||
TotalStake: Currency(company.TotalStake),
|
||||
DeductedStake: Currency(company.DeductedStake),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
ErrInvalidInterval = errors.New("invalid interval provided")
|
||||
)
|
||||
|
||||
type DateInterval string
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -30,18 +30,18 @@ type PaginatedFileResponse struct {
|
|||
// EndDate time.Time
|
||||
// }
|
||||
|
||||
type ReportData struct {
|
||||
TotalBets int64
|
||||
TotalCashIn float64
|
||||
TotalCashOut float64
|
||||
CashBacks float64
|
||||
Withdrawals float64
|
||||
Deposits float64
|
||||
TotalTickets int64
|
||||
VirtualGameStats []VirtualGameStat
|
||||
CompanyReports []CompanyStat
|
||||
BranchReports []BranchStats
|
||||
}
|
||||
// type ReportData struct {
|
||||
// TotalBets int64
|
||||
// TotalCashIn float64
|
||||
// TotalCashOut float64
|
||||
// CashBacks float64
|
||||
// Withdrawals float64
|
||||
// Deposits float64
|
||||
// TotalTickets int64
|
||||
// VirtualGameStats []VirtualGameStat
|
||||
// CompanyReports []CompanyStat
|
||||
// BranchReports []BranchStats
|
||||
// }
|
||||
|
||||
type VirtualGameStat struct {
|
||||
GameName string
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ type ReportRequestDetail struct {
|
|||
RequestedBy ValidInt64
|
||||
RequesterFirstName ValidString
|
||||
RequesterLastName ValidString
|
||||
RequesterRole ValidRole
|
||||
FilePath ValidString
|
||||
Type ReportRequestType
|
||||
Status ReportRequestStatus
|
||||
|
|
@ -69,6 +70,7 @@ type ReportRequestDetailRes struct {
|
|||
RequestedBy *int64 `json:"requested_by,omitempty"`
|
||||
RequesterFirstName *string `json:"requester_first_name,omitempty"`
|
||||
RequesterLastName *string `json:"requester_last_name,omitempty"`
|
||||
RequesterRole *string `json:"requester_role,omitempty"`
|
||||
FilePath *string `json:"file_path,omitempty"`
|
||||
Type ReportRequestType `json:"type"`
|
||||
Status string `json:"status"`
|
||||
|
|
@ -173,6 +175,10 @@ func ConvertDBReportRequestDetail(report dbgen.ReportRequestDetail) (ReportReque
|
|||
Value: report.RequesterLastName.String,
|
||||
Valid: report.RequesterLastName.Valid,
|
||||
},
|
||||
RequesterRole: ValidRole{
|
||||
Value: Role(report.RequesterRole.String),
|
||||
Valid: report.RequesterRole.Valid,
|
||||
},
|
||||
FilePath: ValidString{
|
||||
Value: report.FilePath.String,
|
||||
Valid: report.FilePath.Valid,
|
||||
|
|
@ -257,6 +263,9 @@ func ConvertReportRequestDetail(request ReportRequestDetail) ReportRequestDetail
|
|||
if request.RequesterLastName.Valid {
|
||||
res.RequesterLastName = &request.RequesterLastName.Value
|
||||
}
|
||||
if request.RequesterRole.Valid {
|
||||
res.RequesterRole = (*string)(&request.RequesterRole.Value)
|
||||
}
|
||||
if request.CompanyID.Valid {
|
||||
res.CompanyID = &request.CompanyID.Value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,16 @@ type ReportRequestType string
|
|||
|
||||
var (
|
||||
EventIntervalReportRequest ReportRequestType = "event_interval"
|
||||
EventBetReportRequest ReportRequestType = "event_bet"
|
||||
CompanyReportRequest ReportRequestType = "company"
|
||||
// EventBetReportRequest ReportRequestType = "event_bet"
|
||||
CompanyIntervalReportRequest ReportRequestType = "company_interval"
|
||||
BranchIntervalReportRequest ReportRequestType = "branch_interval"
|
||||
BetIntervalReportRequest ReportRequestType = "bet_interval"
|
||||
WalletIntervalReportRequest ReportRequestType = "wallet_interval"
|
||||
)
|
||||
|
||||
func (r ReportRequestType) IsValid() bool {
|
||||
switch r {
|
||||
case EventIntervalReportRequest, CompanyReportRequest:
|
||||
case EventIntervalReportRequest, CompanyIntervalReportRequest:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -39,20 +39,29 @@ type CustomerWallet struct {
|
|||
CustomerID int64
|
||||
}
|
||||
type GetCustomerWallet struct {
|
||||
ID int64
|
||||
RegularID int64
|
||||
RegularBalance Currency
|
||||
StaticID int64
|
||||
StaticBalance Currency
|
||||
CustomerID int64
|
||||
RegularIsActive bool
|
||||
StaticIsActive bool
|
||||
RegularUpdatedAt time.Time
|
||||
StaticUpdatedAt time.Time
|
||||
CreatedAt time.Time
|
||||
FirstName string
|
||||
LastName string
|
||||
PhoneNumber string
|
||||
ID int64
|
||||
RegularID int64
|
||||
RegularBalance Currency
|
||||
StaticID int64
|
||||
StaticBalance Currency
|
||||
CustomerID int64
|
||||
RegularIsActive bool
|
||||
StaticIsActive bool
|
||||
RegularUpdatedAt time.Time
|
||||
StaticUpdatedAt time.Time
|
||||
CreatedAt time.Time
|
||||
FirstName string
|
||||
LastName string
|
||||
PhoneNumber string
|
||||
NumberOfTransactions int64
|
||||
TotalTransactions Currency
|
||||
NumberOfDeposits int64
|
||||
TotalDepositsAmount Currency
|
||||
NumberOfWithdraws int64
|
||||
TotalWithdrawsAmount Currency
|
||||
NumberOfTransfers int64
|
||||
TotalTransfersAmount Currency
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type BranchWallet struct {
|
||||
|
|
@ -134,21 +143,28 @@ func ConvertCreateCustomerWallet(customerWallet CreateCustomerWallet) dbgen.Crea
|
|||
|
||||
func ConvertDBGetCustomerWallet(customerWallet dbgen.CustomerWalletDetail) GetCustomerWallet {
|
||||
return GetCustomerWallet{
|
||||
ID: customerWallet.ID,
|
||||
RegularID: customerWallet.RegularID,
|
||||
RegularBalance: Currency(customerWallet.RegularBalance),
|
||||
StaticID: customerWallet.StaticID,
|
||||
StaticBalance: Currency(customerWallet.StaticBalance),
|
||||
CustomerID: customerWallet.CustomerID,
|
||||
RegularIsActive: customerWallet.RegularIsActive,
|
||||
StaticIsActive: customerWallet.StaticIsActive,
|
||||
RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time,
|
||||
StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time,
|
||||
CreatedAt: customerWallet.CreatedAt.Time,
|
||||
FirstName: customerWallet.FirstName,
|
||||
LastName: customerWallet.LastName,
|
||||
PhoneNumber: customerWallet.PhoneNumber.String,
|
||||
ID: customerWallet.ID,
|
||||
RegularID: customerWallet.RegularID,
|
||||
RegularBalance: Currency(customerWallet.RegularBalance),
|
||||
StaticID: customerWallet.StaticID,
|
||||
StaticBalance: Currency(customerWallet.StaticBalance),
|
||||
CustomerID: customerWallet.CustomerID,
|
||||
RegularIsActive: customerWallet.RegularIsActive,
|
||||
StaticIsActive: customerWallet.StaticIsActive,
|
||||
RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time,
|
||||
StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time,
|
||||
CreatedAt: customerWallet.CreatedAt.Time,
|
||||
FirstName: customerWallet.FirstName,
|
||||
LastName: customerWallet.LastName,
|
||||
PhoneNumber: customerWallet.PhoneNumber.String,
|
||||
NumberOfTransactions: customerWallet.NumberOfTransactions,
|
||||
TotalTransactions: Currency(customerWallet.TotalTransactions),
|
||||
NumberOfDeposits: customerWallet.NumberOfDeposits,
|
||||
TotalDepositsAmount: Currency(customerWallet.TotalDepositsAmount),
|
||||
NumberOfWithdraws: customerWallet.NumberOfWithdraws,
|
||||
TotalWithdrawsAmount: Currency(customerWallet.TotalWithdrawsAmount),
|
||||
NumberOfTransfers: customerWallet.NumberOfTransfers,
|
||||
TotalTransfersAmount: Currency(customerWallet.TotalTransfersAmount),
|
||||
UpdatedAt: customerWallet.StatsUpdatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
108
internal/domain/wallet_stats.go
Normal file
108
internal/domain/wallet_stats.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
)
|
||||
|
||||
type WalletStat struct {
|
||||
IntervalStart time.Time
|
||||
WalletID int64
|
||||
WalletUserID int64
|
||||
WalletUserFirstName string
|
||||
WalletUserLastName string
|
||||
WalletType string
|
||||
NumberOfTransactions int64
|
||||
TotalTransactions Currency
|
||||
NumberOfDeposits int64
|
||||
TotalDepositsAmount Currency
|
||||
NumberOfWithdraws int64
|
||||
TotalWithdrawsAmount Currency
|
||||
NumberOfTransfers int64
|
||||
TotalTransfersAmount Currency
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type WalletStatRes struct {
|
||||
IntervalStart time.Time `json:"interval_start"`
|
||||
WalletID int64 `json:"wallet_id"`
|
||||
WalletUserID int64 `json:"wallet_user_id"`
|
||||
WalletUserFirstName string `json:"wallet_user_first_name"`
|
||||
WalletUserLastName string `json:"wallet_user_last_name"`
|
||||
WalletType string `json:"wallet_type"`
|
||||
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||
TotalTransactions float32 `json:"total_transactions"`
|
||||
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||
TotalDepositsAmount float32 `json:"total_deposits_amount"`
|
||||
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||
TotalWithdrawsAmount float32 `json:"total_withdraws_amount"`
|
||||
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||
TotalTransfersAmount float32 `json:"total_transfers_amount"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type WalletStatFilter struct {
|
||||
Interval ValidDateInterval
|
||||
UserID ValidInt64
|
||||
}
|
||||
|
||||
func ConvertDBWalletStats(stats dbgen.WalletStat) WalletStat {
|
||||
return WalletStat{
|
||||
IntervalStart: stats.IntervalStart.Time,
|
||||
WalletID: stats.WalletID,
|
||||
WalletUserID: stats.WalletUserID,
|
||||
WalletUserFirstName: stats.WalletUserFirstName,
|
||||
WalletUserLastName: stats.WalletUserLastName,
|
||||
WalletType: stats.WalletType,
|
||||
NumberOfTransactions: stats.NumberOfTransactions,
|
||||
TotalTransactions: Currency(stats.TotalTransactions),
|
||||
NumberOfDeposits: stats.NumberOfDeposits,
|
||||
TotalDepositsAmount: Currency(stats.TotalDepositsAmount),
|
||||
NumberOfWithdraws: stats.NumberOfWithdraws,
|
||||
TotalWithdrawsAmount: Currency(stats.TotalWithdrawsAmount),
|
||||
NumberOfTransfers: stats.NumberOfTransfers,
|
||||
TotalTransfersAmount: Currency(stats.TotalTransfersAmount),
|
||||
UpdatedAt: stats.UpdatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func ConvertDBWalletStatsList(stats []dbgen.WalletStat) []WalletStat {
|
||||
result := make([]WalletStat, len(stats))
|
||||
for i, stat := range stats {
|
||||
result[i] = ConvertDBWalletStats(stat)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ConvertDBWalletStatsByInterval(stats dbgen.GetWalletStatsRow) WalletStat {
|
||||
return WalletStat{
|
||||
IntervalStart: stats.IntervalStart.Time,
|
||||
WalletID: stats.WalletID,
|
||||
WalletUserID: stats.WalletUserID,
|
||||
WalletUserFirstName: stats.WalletUserFirstName,
|
||||
WalletUserLastName: stats.WalletUserLastName,
|
||||
WalletType: stats.WalletType,
|
||||
NumberOfTransactions: stats.NumberOfTransactions,
|
||||
TotalTransactions: Currency(stats.TotalTransactions),
|
||||
NumberOfDeposits: stats.NumberOfDeposits,
|
||||
TotalDepositsAmount: Currency(stats.TotalDepositsAmount),
|
||||
NumberOfWithdraws: stats.NumberOfWithdraws,
|
||||
TotalWithdrawsAmount: Currency(stats.TotalWithdrawsAmount),
|
||||
NumberOfTransfers: stats.NumberOfTransfers,
|
||||
TotalTransfersAmount: Currency(stats.TotalTransfersAmount),
|
||||
UpdatedAt: stats.UpdatedAt.Time,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func ConvertDBWalletStatsByIntervalList(stats []dbgen.GetWalletStatsRow) []WalletStat {
|
||||
result := make([]WalletStat, len(stats))
|
||||
for i, stat := range stats {
|
||||
result[i] = ConvertDBWalletStatsByInterval(stat)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package ports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BetStore interface {
|
||||
|
|
@ -50,4 +51,6 @@ type BetStore interface {
|
|||
UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error
|
||||
}
|
||||
|
||||
|
||||
type BetStatStore interface {
|
||||
GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,3 +32,9 @@ type BranchStore interface {
|
|||
|
||||
GetAllBranchLocations(ctx context.Context, query domain.ValidString) ([]domain.BranchLocation, error)
|
||||
}
|
||||
|
||||
type BranchStatStore interface {
|
||||
UpdateBranchStats(ctx context.Context) error
|
||||
GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error)
|
||||
GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,3 +54,9 @@ type DirectDepositStore interface {
|
|||
GetDirectDepositsByStatus(ctx context.Context, status domain.DirectDepositStatus) ([]domain.DirectDeposit, error)
|
||||
GetCustomerDirectDeposits(ctx context.Context, customerID int64) ([]domain.DirectDeposit, error)
|
||||
}
|
||||
|
||||
type WalletStatStore interface {
|
||||
UpdateWalletStats(ctx context.Context) error
|
||||
GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error)
|
||||
GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error)
|
||||
}
|
||||
|
|
|
|||
23
internal/repository/bet_stats.go
Normal file
23
internal/repository/bet_stats.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
||||
)
|
||||
|
||||
func NewBetStatStore(s *Store) ports.BetStatStore { return s }
|
||||
|
||||
func (s *Store) GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error) {
|
||||
stats, err := s.queries.GetBetStatsByInterval(ctx, dbgen.GetBetStatsByIntervalParams{
|
||||
Interval: filter.Interval.ToPG(),
|
||||
CompanyID: filter.CompanyID.ToPG(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return domain.ConvertDBBetStatsByIntervalList(stats), nil
|
||||
}
|
||||
38
internal/repository/branch_stats.go
Normal file
38
internal/repository/branch_stats.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
||||
)
|
||||
|
||||
// Interface for creating new branch stats store
|
||||
func NewBranchStatStore(s *Store) ports.BranchStatStore { return s }
|
||||
|
||||
func (s *Store) UpdateBranchStats(ctx context.Context) error {
|
||||
return s.queries.UpdateBranchStats(ctx)
|
||||
}
|
||||
|
||||
func (s *Store) GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error) {
|
||||
stats, err := s.queries.GetBranchStatsByID(ctx, branchID)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return domain.ConvertDBBranchStatsList(stats), nil
|
||||
}
|
||||
|
||||
func (s *Store) GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error) {
|
||||
stats, err := s.queries.GetBranchStats(ctx, dbgen.GetBranchStatsParams{
|
||||
Interval: filter.Interval.ToPG(),
|
||||
BranchID: filter.BranchID.ToPG(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return domain.ConvertDBBranchStatsByIntervalList(stats), nil
|
||||
}
|
||||
37
internal/repository/wallet_stats.go
Normal file
37
internal/repository/wallet_stats.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
||||
)
|
||||
|
||||
// Interface for creating new wallet stats store
|
||||
func NewWalletStatStore(s *Store) ports.WalletStatStore { return s }
|
||||
|
||||
func (s *Store) UpdateWalletStats(ctx context.Context) error {
|
||||
return s.queries.UpdateWalletStats(ctx)
|
||||
}
|
||||
|
||||
func (s *Store) GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error) {
|
||||
stats, err := s.queries.GetWalletStatsByID(ctx, walletID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return domain.ConvertDBWalletStatsList(stats), nil
|
||||
}
|
||||
|
||||
func (s *Store) GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error) {
|
||||
stats, err := s.queries.GetWalletStats(ctx, dbgen.GetWalletStatsParams{
|
||||
Interval: filter.Interval.ToPG(),
|
||||
UserID: filter.UserID.ToPG(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return domain.ConvertDBWalletStatsByIntervalList(stats), nil
|
||||
}
|
||||
95
internal/services/report/bet.go
Normal file
95
internal/services/report/bet.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type BetIntervalRow struct {
|
||||
Period string `csv:"Period"`
|
||||
TotalBets int64 `csv:"Total Bets"`
|
||||
TotalStake int64 `csv:"Total Stake"`
|
||||
ActiveBets int64 `csv:"Active Bets"`
|
||||
TotalWins int64 `csv:"Total Wins"`
|
||||
TotalLosses int64 `csv:"Total Losses"`
|
||||
WinBalance int64 `csv:"Win Balance"`
|
||||
NumberOfUnsettled int64 `csv:"Number Of Unsettled"`
|
||||
TotalUnsettledAmount int64 `csv:"Total Unsettled Amount"`
|
||||
TotalShopBets int64 `csv:"Total Shop Bets"`
|
||||
}
|
||||
|
||||
func (s *Service) GenerateBetIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||
if request.Metadata.Interval == nil {
|
||||
s.mongoLogger.Error("[GenerateBetIntervalReport] Metadata interval is empty")
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to parse date interval",
|
||||
zap.String("interval", *request.Metadata.Interval),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
stats, err := s.statService.GetBetStatsByInterval(ctx, domain.BetStatsByIntervalFilter{
|
||||
Interval: domain.ValidDateInterval{
|
||||
Value: interval,
|
||||
Valid: true,
|
||||
},
|
||||
CompanyID: request.CompanyID,
|
||||
})
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to fetch bet stats",
|
||||
zap.String("interval", string(interval)),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", fmt.Errorf("fetching bet stats: %w", err)
|
||||
}
|
||||
|
||||
var rows [][]string
|
||||
var headers []string
|
||||
for _, stat := range stats {
|
||||
endDate, err := domain.GetEndDateFromInterval(interval, stat.Date)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to get end date from interval",
|
||||
zap.String("interval", string(interval)),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", fmt.Errorf("invalid interval end date: %w", err)
|
||||
}
|
||||
|
||||
period := fmt.Sprintf("%s to %s",
|
||||
stat.Date.Format("2006-01-02"),
|
||||
endDate.Format("2006-01-02"),
|
||||
)
|
||||
|
||||
r := BetIntervalRow{
|
||||
Period: period,
|
||||
TotalBets: stat.TotalBets,
|
||||
TotalStake: stat.TotalStake,
|
||||
ActiveBets: stat.ActiveBets,
|
||||
TotalWins: stat.TotalWins,
|
||||
TotalLosses: stat.TotalLosses,
|
||||
WinBalance: stat.WinBalance,
|
||||
NumberOfUnsettled: stat.NumberOfUnsettled,
|
||||
TotalUnsettledAmount: stat.TotalUnsettledAmount,
|
||||
TotalShopBets: stat.TotalShopBets,
|
||||
}
|
||||
|
||||
if headers == nil {
|
||||
headers, _ = StructToCSVRow(r)
|
||||
rows = append(rows, headers)
|
||||
}
|
||||
|
||||
_, row := StructToCSVRow(r)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
return s.WriteCSV(rows, "bet_interval")
|
||||
}
|
||||
99
internal/services/report/branch.go
Normal file
99
internal/services/report/branch.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type BranchIntervalRow struct {
|
||||
Period string `csv:"Period"`
|
||||
BranchName string `csv:"CompanyName"`
|
||||
CompanyName string `csv:"CompanyName"`
|
||||
TotalBets int64 `csv:"Total Bets"`
|
||||
TotalStake float32 `csv:"Total Stake"`
|
||||
DeductedStake float32 `csv:"Deducted Stake"`
|
||||
TotalCashOut float32 `csv:"Total CashOut"`
|
||||
TotalCashBacks float32 `csv:"Total CashBacks"`
|
||||
NumberOfUnsettled int64 `csv:"Number Of Unsettled"`
|
||||
TotalUnsettledAmount float32 `csv:"Total Unsettled Amount"`
|
||||
TotalCashiers int64 `csv:"Total Cashiers"`
|
||||
}
|
||||
|
||||
func (s *Service) GenerateBranchIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||
|
||||
if request.Metadata.Interval == nil {
|
||||
s.mongoLogger.Error("[GenerateBranchIntervalReport] Metadata interval is empty")
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to parse date interval",
|
||||
zap.String("interval", *request.Metadata.Interval),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
stats, err := s.statService.GetBranchStatsByInterval(ctx, domain.BranchStatFilter{
|
||||
Interval: domain.ValidDateInterval{
|
||||
Value: interval,
|
||||
Valid: true,
|
||||
},
|
||||
CompanyID: request.CompanyID,
|
||||
BranchID: domain.ConvertInt64Ptr(request.Metadata.BranchID),
|
||||
})
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to fetch branch stats",
|
||||
zap.String("interval", string(interval)),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", fmt.Errorf("fetching branch stats: %w", err)
|
||||
}
|
||||
|
||||
var rows [][]string
|
||||
var headers []string
|
||||
for _, stat := range stats {
|
||||
endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to get end date from interval",
|
||||
zap.String("interval", string(interval)),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", fmt.Errorf("invalid interval end date: %w", err)
|
||||
}
|
||||
|
||||
period := fmt.Sprintf("%s to %s",
|
||||
stat.IntervalStart.Format("2006-01-02"),
|
||||
endDate.Format("2006-01-02"),
|
||||
)
|
||||
|
||||
r := BranchIntervalRow{
|
||||
Period: period,
|
||||
BranchName: stat.BranchName,
|
||||
CompanyName: stat.CompanyName,
|
||||
TotalBets: stat.TotalBets,
|
||||
TotalStake: stat.TotalStake.Float32(),
|
||||
DeductedStake: stat.DeductedStake.Float32(),
|
||||
TotalCashOut: stat.TotalCashOut.Float32(),
|
||||
TotalCashBacks: stat.TotalCashBacks.Float32(),
|
||||
NumberOfUnsettled: stat.NumberOfUnsettled,
|
||||
TotalUnsettledAmount: stat.TotalUnsettledAmount.Float32(),
|
||||
TotalCashiers: stat.TotalCashiers,
|
||||
}
|
||||
|
||||
if headers == nil {
|
||||
headers, _ = StructToCSVRow(r)
|
||||
rows = append(rows, headers)
|
||||
}
|
||||
|
||||
_, row := StructToCSVRow(r)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
return s.WriteCSV(rows, "branch_interval")
|
||||
}
|
||||
111
internal/services/report/company.go
Normal file
111
internal/services/report/company.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type CompanyIntervalRow struct {
|
||||
Period string `csv:"Period"`
|
||||
CompanyName string `csv:"CompanyName"`
|
||||
TotalBets int64 `csv:"Total Bets"`
|
||||
TotalStake float32 `csv:"Total Stake"`
|
||||
DeductedStake float32 `csv:"Deducted Stake"`
|
||||
TotalCashOut float32 `csv:"Total CashOut"`
|
||||
TotalCashBacks float32 `csv:"Total CashBacks"`
|
||||
NumberOfUnsettled int64 `csv:"Number Of Unsettled"`
|
||||
TotalUnsettledAmount float32 `csv:"Total Unsettled Amount"`
|
||||
TotalAdmins int64 `csv:"Total Admins"`
|
||||
TotalManagers int64 `csv:"Total Managers"`
|
||||
TotalCashiers int64 `csv:"Total Cashiers"`
|
||||
TotalCustomers int64 `csv:"Total Customers"`
|
||||
TotalApprovers int64 `csv:"Total Approvers"`
|
||||
TotalBranches int64 `csv:"Total Branches"`
|
||||
}
|
||||
|
||||
func (s *Service) GenerateCompanyIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||
|
||||
// Only a super-admin is allowed to generate this type of report
|
||||
if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin {
|
||||
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Unauthorized user report")
|
||||
return "", ErrUnauthorizedUserReport
|
||||
}
|
||||
|
||||
if request.Metadata.Interval == nil {
|
||||
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Metadata interval is empty")
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to parse date interval",
|
||||
zap.String("interval", *request.Metadata.Interval),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
stats, err := s.statService.GetCompanyStatsByInterval(ctx, domain.CompanyStatFilter{
|
||||
Interval: domain.ValidDateInterval{
|
||||
Value: interval,
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to fetch company stats",
|
||||
zap.String("interval", string(interval)),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", fmt.Errorf("fetching company stats: %w", err)
|
||||
}
|
||||
|
||||
var rows [][]string
|
||||
var headers []string
|
||||
for _, stat := range stats {
|
||||
endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to get end date from interval",
|
||||
zap.String("interval", string(interval)),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", fmt.Errorf("invalid interval end date: %w", err)
|
||||
}
|
||||
|
||||
period := fmt.Sprintf("%s to %s",
|
||||
stat.IntervalStart.Format("2006-01-02"),
|
||||
endDate.Format("2006-01-02"),
|
||||
)
|
||||
|
||||
r := CompanyIntervalRow{
|
||||
Period: period,
|
||||
CompanyName: stat.CompanyName,
|
||||
TotalBets: stat.TotalBets,
|
||||
TotalStake: stat.TotalStake.Float32(),
|
||||
DeductedStake: stat.DeductedStake.Float32(),
|
||||
TotalCashOut: stat.TotalCashOut.Float32(),
|
||||
TotalCashBacks: stat.TotalCashBacks.Float32(),
|
||||
NumberOfUnsettled: stat.NumberOfUnsettled,
|
||||
TotalUnsettledAmount: stat.TotalUnsettledAmount.Float32(),
|
||||
TotalAdmins: stat.TotalAdmins,
|
||||
TotalManagers: stat.TotalManagers,
|
||||
TotalCashiers: stat.TotalCashiers,
|
||||
TotalCustomers: stat.TotalCustomers,
|
||||
TotalApprovers: stat.TotalApprovers,
|
||||
TotalBranches: stat.TotalBranches,
|
||||
}
|
||||
|
||||
if headers == nil {
|
||||
headers, _ = StructToCSVRow(r)
|
||||
rows = append(rows, headers)
|
||||
}
|
||||
|
||||
_, row := StructToCSVRow(r)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
return s.WriteCSV(rows, "company_interval")
|
||||
}
|
||||
|
|
@ -8,12 +8,14 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
"reflect"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
ErrReportFileNotFound = errors.New("failed to find report file")
|
||||
ErrReportFileError = errors.New("unknown error with report file")
|
||||
|
|
@ -21,6 +23,20 @@ var (
|
|||
ErrReportFilePathInvalid = errors.New("report file path is invalid")
|
||||
)
|
||||
|
||||
func StructToCSVRow(v any) ([]string, []string) {
|
||||
t := reflect.TypeOf(v)
|
||||
val := reflect.ValueOf(v)
|
||||
headers := make([]string, t.NumField())
|
||||
row := make([]string, t.NumField())
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
headers[i] = field.Tag.Get("csv")
|
||||
row[i] = fmt.Sprint(val.Field(i).Interface())
|
||||
}
|
||||
return headers, row
|
||||
}
|
||||
|
||||
func (s *Service) WriteCSV(rows [][]string, filePrefix string) (string, error) {
|
||||
if len(rows) == 0 {
|
||||
s.mongoLogger.Error("[WriteCSV] CSV with no data",
|
||||
|
|
|
|||
|
|
@ -3,21 +3,44 @@ package report
|
|||
import (
|
||||
"context"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidInterval = errors.New("invalid interval provided")
|
||||
)
|
||||
type EventIntervalRow struct {
|
||||
Period string `csv:"Period"`
|
||||
TotalEvents int64 `csv:"Total Events"`
|
||||
ActiveEvents int64 `csv:"Active Events"`
|
||||
InActiveEvents int64 `csv:"In-Active Events"`
|
||||
FeaturedEvents int64 `csv:"Featured Events"`
|
||||
Leagues int64 `csv:"Leagues"`
|
||||
Pending int64 `csv:"Pending"`
|
||||
InPlay int64 `csv:"In-Play"`
|
||||
ToBeFixed int64 `csv:"To-Be-Fixed"`
|
||||
Ended int64 `csv:"Ended"`
|
||||
Postponed int64 `csv:"Postponed"`
|
||||
Cancelled int64 `csv:"Cancelled"`
|
||||
Walkover int64 `csv:"Walkover"`
|
||||
Interrupted int64 `csv:"Interrupted"`
|
||||
Abandoned int64 `csv:"Abandoned"`
|
||||
Retired int64 `csv:"Retired"`
|
||||
Suspended int64 `csv:"Suspended"`
|
||||
DecidedByFA int64 `csv:"Decided-By-FA"`
|
||||
Removed int64 `csv:"Removed"`
|
||||
}
|
||||
|
||||
func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||
// Only a super-admin is allowed to generate this type of report
|
||||
if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin {
|
||||
s.mongoLogger.Error("[GenerateEventIntervalReport] Unauthorized user report")
|
||||
return "", ErrUnauthorizedUserReport
|
||||
}
|
||||
|
||||
if request.Metadata.Interval == nil {
|
||||
s.mongoLogger.Error("[GenerateEventIntervalReport] Metadata interval is empty")
|
||||
return "", ErrInvalidInterval
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||
|
|
@ -26,7 +49,7 @@ func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domai
|
|||
zap.String("interval", *request.Metadata.Interval),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", ErrInvalidInterval
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
stats, err := s.statService.GetTotalEventStatsByInterval(ctx, domain.EventStatsByIntervalFilter{
|
||||
|
|
@ -43,12 +66,8 @@ func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domai
|
|||
return "", fmt.Errorf("fetching event stats: %w", err)
|
||||
}
|
||||
|
||||
rows := [][]string{{
|
||||
"Period", "Total Events", "Active Events", "In-Active Events", "Featured Events", "Leagues",
|
||||
"Pending", "In-Play", "To-Be-Fixed", "Ended", "Postponed", "Cancelled", "Walkover",
|
||||
"Interrupted", "Abandoned", "Retired", "Suspended", "Decided-By-FA", "Removed",
|
||||
}}
|
||||
|
||||
var rows [][]string
|
||||
var headers []string
|
||||
for _, stat := range stats {
|
||||
endDate, err := domain.GetEndDateFromInterval(interval, stat.Date)
|
||||
if err != nil {
|
||||
|
|
@ -64,27 +83,35 @@ func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domai
|
|||
endDate.Format("2006-01-02"),
|
||||
)
|
||||
|
||||
rows = append(rows, []string{
|
||||
period,
|
||||
fmt.Sprint(stat.EventCount),
|
||||
fmt.Sprint(stat.TotalActiveEvents),
|
||||
fmt.Sprint(stat.TotalInActiveEvents),
|
||||
fmt.Sprint(stat.TotalFeaturedEvents),
|
||||
fmt.Sprint(stat.TotalLeagues),
|
||||
fmt.Sprint(stat.Pending),
|
||||
fmt.Sprint(stat.InPlay),
|
||||
fmt.Sprint(stat.ToBeFixed),
|
||||
fmt.Sprint(stat.Ended),
|
||||
fmt.Sprint(stat.Postponed),
|
||||
fmt.Sprint(stat.Cancelled),
|
||||
fmt.Sprint(stat.Walkover),
|
||||
fmt.Sprint(stat.Interrupted),
|
||||
fmt.Sprint(stat.Abandoned),
|
||||
fmt.Sprint(stat.Retired),
|
||||
fmt.Sprint(stat.Suspended),
|
||||
fmt.Sprint(stat.DecidedByFa),
|
||||
fmt.Sprint(stat.Removed),
|
||||
})
|
||||
r := EventIntervalRow{
|
||||
Period: period,
|
||||
TotalEvents: stat.EventCount,
|
||||
ActiveEvents: stat.TotalActiveEvents,
|
||||
InActiveEvents: stat.TotalInActiveEvents,
|
||||
FeaturedEvents: stat.TotalFeaturedEvents,
|
||||
Leagues: stat.TotalLeagues,
|
||||
Pending: stat.Pending,
|
||||
InPlay: stat.InPlay,
|
||||
ToBeFixed: stat.ToBeFixed,
|
||||
Ended: stat.Ended,
|
||||
Postponed: stat.Postponed,
|
||||
Cancelled: stat.Cancelled,
|
||||
Walkover: stat.Walkover,
|
||||
Interrupted: stat.Interrupted,
|
||||
Abandoned: stat.Abandoned,
|
||||
Retired: stat.Retired,
|
||||
Suspended: stat.Suspended,
|
||||
DecidedByFA: stat.DecidedByFa,
|
||||
Removed: stat.Removed,
|
||||
}
|
||||
|
||||
if headers == nil {
|
||||
headers, _ = StructToCSVRow(r)
|
||||
rows = append(rows, headers)
|
||||
}
|
||||
|
||||
_, row := StructToCSVRow(r)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
return s.WriteCSV(rows, "event_interval")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package report
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
|
@ -9,6 +10,11 @@ import (
|
|||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
ErrUnauthorizedUserReport = errors.New("unauthorized user report")
|
||||
)
|
||||
|
||||
func (s *Service) ProcessReportRequests(ctx context.Context) error {
|
||||
requests, total, err := s.GetAllReportRequests(ctx, domain.ReportRequestFilter{
|
||||
Status: domain.ValidReportRequestStatus{
|
||||
|
|
|
|||
|
|
@ -95,9 +95,11 @@ type ReportGeneratorFunc func(ctx context.Context, req domain.ReportRequestDetai
|
|||
|
||||
func (s *Service) registerGenerators() {
|
||||
s.generators = map[domain.ReportRequestType]ReportGeneratorFunc{
|
||||
domain.EventIntervalReportRequest: s.GenerateEventIntervalReport,
|
||||
// domain.CompanySummaryReportRequest: s.GenerateCompanySummaryReport,
|
||||
// domain.BranchPerformanceReportRequest: s.GenerateBranchPerformanceReport,
|
||||
domain.EventIntervalReportRequest: s.GenerateEventIntervalReport,
|
||||
domain.CompanyIntervalReportRequest: s.GenerateCompanyIntervalReport,
|
||||
domain.BranchIntervalReportRequest: s.GenerateBranchIntervalReport,
|
||||
domain.BetIntervalReportRequest: s.GenerateBetIntervalReport,
|
||||
domain.WalletIntervalReportRequest: s.GenerateWalletIntervalReport,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
107
internal/services/report/wallet.go
Normal file
107
internal/services/report/wallet.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type WalletIntervalRow struct {
|
||||
Period string `csv:"Period"`
|
||||
WalletUserFirstName string `csv:"User First Name"`
|
||||
WalletUserLastName string `csv:"User Last Name"`
|
||||
WalletID int64 `csv:"Wallet Id"`
|
||||
WalletType string `csv:"Wallet Type"`
|
||||
NumberOfTransactions int64 `csv:"Number Of Transactions"`
|
||||
TotalTransactions float32 `csv:"Total Transactions"`
|
||||
NumberOfDeposits int64 `csv:"Number Of Deposits"`
|
||||
TotalDepositsAmount float32 `csv:"Total Deposits Amount"`
|
||||
NumberOfWithdraws int64 `csv:"Number Of Withdraws"`
|
||||
TotalWithdrawsAmount float32 `csv:"Total Withdraws Amount"`
|
||||
NumberOfTransfers int64 `csv:"Number Of Transfers"`
|
||||
TotalTransfersAmount float32 `csv:"Total Transfers Amount"`
|
||||
}
|
||||
|
||||
func (s *Service) GenerateWalletIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||
|
||||
// Only a super-admin is allowed to generate this type of report
|
||||
if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin {
|
||||
s.mongoLogger.Error("[GenerateWalletIntervalReport] Unauthorized user report")
|
||||
return "", ErrUnauthorizedUserReport
|
||||
}
|
||||
|
||||
if request.Metadata.Interval == nil {
|
||||
s.mongoLogger.Error("[GenerateWalletIntervalReport] Metadata interval is empty")
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to parse date interval",
|
||||
zap.String("interval", *request.Metadata.Interval),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", domain.ErrInvalidInterval
|
||||
}
|
||||
|
||||
stats, err := s.statService.GetWalletStatsByInterval(ctx, domain.WalletStatFilter{
|
||||
Interval: domain.ValidDateInterval{
|
||||
Value: interval,
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to fetch wallet stats",
|
||||
zap.String("interval", string(interval)),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", fmt.Errorf("fetching wallet stats: %w", err)
|
||||
}
|
||||
|
||||
var rows [][]string
|
||||
var headers []string
|
||||
for _, stat := range stats {
|
||||
endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart)
|
||||
if err != nil {
|
||||
s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to get end date from interval",
|
||||
zap.String("interval", string(interval)),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", fmt.Errorf("invalid interval end date: %w", err)
|
||||
}
|
||||
|
||||
period := fmt.Sprintf("%s to %s",
|
||||
stat.IntervalStart.Format("2006-01-02"),
|
||||
endDate.Format("2006-01-02"),
|
||||
)
|
||||
|
||||
r := WalletIntervalRow{
|
||||
Period: period,
|
||||
WalletUserFirstName: stat.WalletUserFirstName,
|
||||
WalletUserLastName: stat.WalletUserLastName,
|
||||
WalletID: stat.WalletID,
|
||||
WalletType: stat.WalletType,
|
||||
NumberOfTransactions: stat.NumberOfTransactions,
|
||||
TotalTransactions: stat.TotalTransactions.Float32(),
|
||||
NumberOfDeposits: stat.NumberOfDeposits,
|
||||
TotalDepositsAmount: stat.TotalDepositsAmount.Float32(),
|
||||
NumberOfWithdraws: stat.NumberOfWithdraws,
|
||||
TotalWithdrawsAmount: stat.TotalWithdrawsAmount.Float32(),
|
||||
NumberOfTransfers: stat.NumberOfTransfers,
|
||||
TotalTransfersAmount: stat.TotalTransfersAmount.Float32(),
|
||||
}
|
||||
|
||||
if headers == nil {
|
||||
headers, _ = StructToCSVRow(r)
|
||||
rows = append(rows, headers)
|
||||
}
|
||||
|
||||
_, row := StructToCSVRow(r)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
return s.WriteCSV(rows, "wallet_interval")
|
||||
}
|
||||
11
internal/services/stats/bet.go
Normal file
11
internal/services/stats/bet.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
||||
func (s *Service) GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error) {
|
||||
return s.betStatStore.GetBetStatsByInterval(ctx, filter)
|
||||
}
|
||||
16
internal/services/stats/branch.go
Normal file
16
internal/services/stats/branch.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
||||
func (s *Service) UpdateBranchStats(ctx context.Context) error {
|
||||
return s.branchStatStore.UpdateBranchStats(ctx)
|
||||
}
|
||||
func (s *Service) GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error) {
|
||||
return s.branchStatStore.GetBranchStatByID(ctx, branchID)
|
||||
}
|
||||
func (s *Service) GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error) {
|
||||
return s.branchStatStore.GetBranchStatsByInterval(ctx, filter)
|
||||
}
|
||||
|
|
@ -6,11 +6,11 @@ import (
|
|||
)
|
||||
|
||||
func (s *Service) UpdateCompanyStats(ctx context.Context) error {
|
||||
return s.UpdateCompanyStats(ctx)
|
||||
return s.companyStatStore.UpdateCompanyStats(ctx)
|
||||
}
|
||||
func (s *Service) GetCompanyStatByID(ctx context.Context, companyID int64) ([]domain.CompanyStat, error) {
|
||||
return s.GetCompanyStatByID(ctx, companyID)
|
||||
return s.companyStatStore.GetCompanyStatByID(ctx, companyID)
|
||||
}
|
||||
func (s *Service) GetCompanyStatsByInterval(ctx context.Context, filter domain.CompanyStatFilter) ([]domain.CompanyStat, error) {
|
||||
return s.GetCompanyStatsByInterval(ctx, filter)
|
||||
return s.companyStatStore.GetCompanyStatsByInterval(ctx, filter)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,24 @@ import "github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
|||
|
||||
type Service struct {
|
||||
companyStatStore ports.CompanyStatStore
|
||||
eventStatStore ports.EventStatStore
|
||||
branchStatStore ports.BranchStatStore
|
||||
eventStatStore ports.EventStatStore
|
||||
betStatStore ports.BetStatStore
|
||||
walletStatStore ports.WalletStatStore
|
||||
}
|
||||
|
||||
func NewService(companyStatStore ports.CompanyStatStore, eventStatStore ports.EventStatStore) *Service {
|
||||
func NewService(
|
||||
companyStatStore ports.CompanyStatStore,
|
||||
branchStatStore ports.BranchStatStore,
|
||||
eventStatStore ports.EventStatStore,
|
||||
betStatStore ports.BetStatStore,
|
||||
walletStatStore ports.WalletStatStore,
|
||||
) *Service {
|
||||
return &Service{
|
||||
companyStatStore: companyStatStore,
|
||||
eventStatStore: eventStatStore,
|
||||
branchStatStore: branchStatStore,
|
||||
eventStatStore: eventStatStore,
|
||||
betStatStore: betStatStore,
|
||||
walletStatStore: walletStatStore,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
internal/services/stats/wallet.go
Normal file
16
internal/services/stats/wallet.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
||||
func (s *Service) UpdateWalletStats(ctx context.Context) error {
|
||||
return s.walletStatStore.UpdateWalletStats(ctx)
|
||||
}
|
||||
func (s *Service) GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error) {
|
||||
return s.walletStatStore.GetWalletStatByID(ctx, walletID)
|
||||
}
|
||||
func (s *Service) GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error) {
|
||||
return s.walletStatStore.GetWalletStatsByInterval(ctx, filter)
|
||||
}
|
||||
|
|
@ -235,6 +235,44 @@ func StartStatCrons(statService *stats.Service, mongoLogger *zap.Logger) {
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
spec: "0 0 * * * *", // Every hour
|
||||
task: func() {
|
||||
start := time.Now()
|
||||
mongoLogger.Info("[Branch Stats Crons] Updating branch stats", zap.Time("timestamp", time.Now()))
|
||||
if err := statService.UpdateBranchStats(context.Background()); err != nil {
|
||||
mongoLogger.Error("[Branch Stats Crons] Failed to update branch stats",
|
||||
zap.Error(err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
zap.Duration("duration", time.Since(start)),
|
||||
)
|
||||
} else {
|
||||
mongoLogger.Info("[Branch Stats Crons] Successfully updated branch stats",
|
||||
zap.Time("timestamp", time.Now()),
|
||||
zap.Duration("duration", time.Since(start)),
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
spec: "0 0 * * * *", // Every hour
|
||||
task: func() {
|
||||
start := time.Now()
|
||||
mongoLogger.Info("[Wallet Stats Crons] Updating wallet stats", zap.Time("timestamp", time.Now()))
|
||||
if err := statService.UpdateWalletStats(context.Background()); err != nil {
|
||||
mongoLogger.Error("[Wallet Stats Crons] Failed to update wallet stats",
|
||||
zap.Error(err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
zap.Duration("duration", time.Since(start)),
|
||||
)
|
||||
} else {
|
||||
mongoLogger.Info("[Wallet Stats Crons] Successfully updated wallet stats",
|
||||
zap.Time("timestamp", time.Now()),
|
||||
zap.Duration("duration", time.Since(start)),
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
spec: "0 0 * * * *", // Hourly
|
||||
task: func() {
|
||||
|
|
|
|||
|
|
@ -38,38 +38,56 @@ func convertWallet(wallet domain.Wallet) WalletRes {
|
|||
}
|
||||
|
||||
type CustomerWalletRes struct {
|
||||
ID int64 `json:"id" example:"1"`
|
||||
RegularID int64 `json:"regular_id" example:"1"`
|
||||
RegularBalance float32 `json:"regular_balance" example:"100.0"`
|
||||
StaticID int64 `json:"static_id" example:"1"`
|
||||
StaticBalance float32 `json:"static_balance" example:"100.0"`
|
||||
CustomerID int64 `json:"customer_id" example:"1"`
|
||||
RegularIsActive bool `json:"regular_is_active" example:"true"`
|
||||
StaticIsActive bool `json:"static_is_active" example:"true"`
|
||||
RegularUpdatedAt time.Time `json:"regular_updated_at"`
|
||||
StaticUpdatedAt time.Time `json:"static_updated_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
FirstName string `json:"first_name" example:"John"`
|
||||
LastName string `json:"last_name" example:"Smith"`
|
||||
PhoneNumber string `json:"phone_number" example:"0911111111"`
|
||||
ID int64 `json:"id" example:"1"`
|
||||
RegularID int64 `json:"regular_id" example:"1"`
|
||||
RegularBalance float32 `json:"regular_balance" example:"100.0"`
|
||||
StaticID int64 `json:"static_id" example:"1"`
|
||||
StaticBalance float32 `json:"static_balance" example:"100.0"`
|
||||
CustomerID int64 `json:"customer_id" example:"1"`
|
||||
RegularIsActive bool `json:"regular_is_active" example:"true"`
|
||||
StaticIsActive bool `json:"static_is_active" example:"true"`
|
||||
RegularUpdatedAt time.Time `json:"regular_updated_at"`
|
||||
StaticUpdatedAt time.Time `json:"static_updated_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
FirstName string `json:"first_name" example:"John"`
|
||||
LastName string `json:"last_name" example:"Smith"`
|
||||
PhoneNumber string `json:"phone_number" example:"0911111111"`
|
||||
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||
TotalTransactions float32 `json:"total_transactions"`
|
||||
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||
TotalDepositsAmount float32 `json:"total_deposits_amount"`
|
||||
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||
TotalWithdrawsAmount float32 `json:"total_withdraws_amount"`
|
||||
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||
TotalTransfersAmount float32 `json:"total_transfers_amount"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func ConvertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes {
|
||||
return CustomerWalletRes{
|
||||
ID: wallet.ID,
|
||||
RegularID: wallet.RegularID,
|
||||
RegularBalance: wallet.RegularBalance.Float32(),
|
||||
StaticID: wallet.StaticID,
|
||||
StaticBalance: wallet.StaticBalance.Float32(),
|
||||
CustomerID: wallet.CustomerID,
|
||||
RegularIsActive: wallet.RegularIsActive,
|
||||
StaticIsActive: wallet.StaticIsActive,
|
||||
RegularUpdatedAt: wallet.RegularUpdatedAt,
|
||||
StaticUpdatedAt: wallet.StaticUpdatedAt,
|
||||
CreatedAt: wallet.CreatedAt,
|
||||
FirstName: wallet.FirstName,
|
||||
LastName: wallet.LastName,
|
||||
PhoneNumber: wallet.PhoneNumber,
|
||||
ID: wallet.ID,
|
||||
RegularID: wallet.RegularID,
|
||||
RegularBalance: wallet.RegularBalance.Float32(),
|
||||
StaticID: wallet.StaticID,
|
||||
StaticBalance: wallet.StaticBalance.Float32(),
|
||||
CustomerID: wallet.CustomerID,
|
||||
RegularIsActive: wallet.RegularIsActive,
|
||||
StaticIsActive: wallet.StaticIsActive,
|
||||
RegularUpdatedAt: wallet.RegularUpdatedAt,
|
||||
StaticUpdatedAt: wallet.StaticUpdatedAt,
|
||||
CreatedAt: wallet.CreatedAt,
|
||||
FirstName: wallet.FirstName,
|
||||
LastName: wallet.LastName,
|
||||
PhoneNumber: wallet.PhoneNumber,
|
||||
NumberOfTransactions: wallet.NumberOfTransactions,
|
||||
TotalTransactions: wallet.TotalTransactions.Float32(),
|
||||
NumberOfDeposits: wallet.NumberOfDeposits,
|
||||
TotalDepositsAmount: wallet.TotalDepositsAmount.Float32(),
|
||||
NumberOfWithdraws: wallet.NumberOfWithdraws,
|
||||
TotalWithdrawsAmount: wallet.TotalWithdrawsAmount.Float32(),
|
||||
NumberOfTransfers: wallet.NumberOfTransfers,
|
||||
TotalTransfersAmount: wallet.TotalTransfersAmount.Float32(),
|
||||
UpdatedAt: wallet.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user