diff --git a/cmd/main.go b/cmd/main.go index ef59a25..1a336b9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -146,7 +146,7 @@ func main() { vitualGameRepo := repository.NewVirtualGameRepository(store) recommendationRepo := repository.NewRecommendationRepository(store) - referalSvc := referralservice.New(referalRepo, *walletSvc, store, cfg, logger) + referalSvc := referralservice.New(referalRepo, *walletSvc, *settingSvc, cfg, logger) virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger) aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger) veliCLient := veli.NewClient(cfg, walletSvc) diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index 898d587..ed75d50 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -193,7 +193,8 @@ CREATE TABLE IF NOT EXISTS wallets ( type VARCHAR(255) NOT NULL, is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT balance_positve CHECK (balance >= 0) ); CREATE TABLE IF NOT EXISTS customer_wallets ( id BIGSERIAL PRIMARY KEY, diff --git a/db/migrations/000003_referal.up.sql b/db/migrations/000003_referal.up.sql index dc51f2a..c6cbd92 100644 --- a/db/migrations/000003_referal.up.sql +++ b/db/migrations/000003_referal.up.sql @@ -1,48 +1,40 @@ -CREATE TYPE ReferralStatus AS ENUM ('PENDING', 'COMPLETED', 'EXPIRED', 'CANCELLED'); -CREATE TABLE IF NOT EXISTS referral_settings ( +-- CREATE TYPE ReferralStatus AS ENUM ('PENDING', 'COMPLETED', 'EXPIRED', 'CANCELLED'); +-- CREATE TABLE IF NOT EXISTS referral_settings ( +-- id BIGSERIAL PRIMARY KEY, +-- referral_reward_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00, +-- cashback_percentage DECIMAL(5, 2) NOT NULL DEFAULT 0.00, +-- bet_referral_bonus_percentage NUMERIC DEFAULT 5.0, +-- max_referrals INTEGER NOT NULL DEFAULT 0, +-- expires_after_days INTEGER NOT NULL DEFAULT 30, +-- updated_by VARCHAR(255) NOT NULL, +-- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, +-- updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, +-- version INTEGER NOT NULL DEFAULT 0, +-- CONSTRAINT referral_reward_amount_positive CHECK (referral_reward_amount >= 0), +-- CONSTRAINT cashback_percentage_range CHECK ( +-- cashback_percentage >= 0 +-- AND cashback_percentage <= 100 +-- ) +-- ); + +CREATE TABLE IF NOT EXISTS referral_codes ( id BIGSERIAL PRIMARY KEY, - referral_reward_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00, - cashback_percentage DECIMAL(5, 2) NOT NULL DEFAULT 0.00, - bet_referral_bonus_percentage NUMERIC DEFAULT 5.0, - max_referrals INTEGER NOT NULL DEFAULT 0, - expires_after_days INTEGER NOT NULL DEFAULT 30, - updated_by VARCHAR(255) NOT NULL, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - version INTEGER NOT NULL DEFAULT 0, - CONSTRAINT referral_reward_amount_positive CHECK (referral_reward_amount >= 0), - CONSTRAINT cashback_percentage_range CHECK ( - cashback_percentage >= 0 - AND cashback_percentage <= 100 - ) -); -CREATE TABLE IF NOT EXISTS referrals ( - id BIGSERIAL PRIMARY KEY, - company_id BIGINT NOT NULL REFERENCES companies (id) ON -DELETE CASCADE, referral_code VARCHAR(10) NOT NULL UNIQUE, - referrer_id BIGINT NOT NULL REFERENCES users (id), - referred_id BIGINT UNIQUE REFERENCES users (id), - status ReferralStatus NOT NULL DEFAULT 'PENDING', - reward_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00, - cashback_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00, + referrer_id BIGINT NOT NULL UNIQUE REFERENCES users (id), + company_id BIGINT NOT NULL REFERENCES companies (id), + is_active BOOLEAN NOT NULL DEFAULT true, + number_of_referrals BIGINT NOT NULL, + reward_amount BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - expires_at TIMESTAMPTZ NOT NULL -- FOREIGN KEY (referrer_id) REFERENCES users (id), - -- FOREIGN KEY (referred_id) REFERENCES users (id), -, - CONSTRAINT reward_amount_positive CHECK (reward_amount >= 0), - CONSTRAINT cashback_amount_positive CHECK (cashback_amount >= 0) + CONSTRAINT reward_amount_positive CHECK (reward_amount >= 0) ); -CREATE INDEX idx_referrals_referral_code ON referrals (referral_code); CREATE INDEX idx_referrals_referrer_id ON referrals (referrer_id); CREATE INDEX idx_referrals_status ON referrals (status); -ALTER TABLE users -ADD COLUMN IF NOT EXISTS referral_code VARCHAR(10) UNIQUE, - ADD COLUMN IF NOT EXISTS referred_by VARCHAR(10); --- Modify wallet TABLE to track bonus money separately -ALTER TABLE wallets -ADD COLUMN IF NOT EXISTS bonus_balance DECIMAL(15, 2) NOT NULL DEFAULT 0.00, - ADD COLUMN IF NOT EXISTS cash_balance DECIMAL(15, 2) NOT NULL DEFAULT 0.00, - ADD CONSTRAINT bonus_balance_positive CHECK (bonus_balance >= 0), - ADD CONSTRAINT cash_balance_positive CHECK (cash_balance >= 0); \ No newline at end of file + +CREATE TABLE IF NOT EXISTS user_referrals ( + id BIGSERIAL PRIMARY KEY, + referred_id BIGINT UNIQUE NOT NULL REFERENCES users (id), + referral_code_id BIGINT NOT NULL REFERENCES referral_codes (id), + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/db/query/referal.sql b/db/query/referal.sql index 6c60722..10e5781 100644 --- a/db/query/referal.sql +++ b/db/query/referal.sql @@ -1,87 +1,51 @@ --- name: CreateReferral :one -INSERT INTO referrals ( +-- name: CreateReferralCode :one +INSERT INTO referral_codes ( referral_code, referrer_id, company_id, - status, - reward_amount, - expires_at + number_of_referrals, + reward_amount ) -VALUES ($1, $2, $3, $4, $5, $6) +VALUES ($1, $2, $3, $4, $5) RETURNING *; --- name: GetReferralByCode :one +-- name: CreateUserReferral :one +INSERT INTO user_referrals (referred_id, referral_code_id) +VALUES ($1, $2) +RETURNING *; +-- name: GetReferralCodeByUser :many +SELECt * +FROM referral_codes +WHERE referrer_id = $1; +-- name: GetReferralCode :one SELECT * -FROM referrals +FROM referral_codes WHERE referral_code = $1; --- name: UpdateReferral :one -UPDATE referrals -SET referred_id = $2, - status = $3, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -RETURNING *; -- name: UpdateReferralCode :exec -UPDATE users -SET referral_code = $2, +UPDATE referral_codes +SET is_active = $2, + referral_code = $3, + number_of_referrals = $4, + reward_amount = $5, updated_at = CURRENT_TIMESTAMP WHERE id = $1; -- name: GetReferralStats :one SELECT COUNT(*) AS total_referrals, - COUNT( - CASE - WHEN status = 'COMPLETED' THEN 1 - END - ) AS completed_referrals, - COALESCE(SUM(reward_amount), 0) AS total_reward_earned, - COALESCE( - SUM( - CASE - WHEN status = 'PENDING' THEN reward_amount - END - ), - 0 - ) AS pending_rewards -FROM referrals + SUM(reward_amount) AS total_reward_earned +FROM user_referrals + JOIN referral_codes ON referral_codes.id == referral_code_id WHERE referrer_id = $1 AND company_id = $2; --- name: GetReferralSettings :one +-- name: GetUserReferral :one SELECT * -FROM referral_settings -LIMIT 1; --- name: UpdateReferralSettings :one -UPDATE referral_settings -SET referral_reward_amount = $2, - cashback_percentage = $3, - bet_referral_bonus_percentage = $4, - max_referrals = $5, - expires_after_days = $6, - updated_by = $7, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -RETURNING *; --- name: CreateReferralSettings :one -INSERT INTO referral_settings ( - referral_reward_amount, - cashback_percentage, - max_referrals, - bet_referral_bonus_percentage, - expires_after_days, - updated_by - ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING *; --- name: GetReferralByReferredID :one -SELECT * -FROM referrals -WHERE referred_id = $1 -LIMIT 1; --- name: GetActiveReferralByReferrerID :one -SELECT * -FROM referrals -WHERE referrer_id = $1 - AND status = 'PENDING' -LIMIT 1; --- name: GetReferralCountByID :one +FROM user_referrals +WHERE referred_id = $1; +-- name: GetUserReferralsByCode :many +SELECT user_referrals.* +FROM user_referrals + JOIN referral_codes ON referral_codes.id == referral_code_id +WHERE referral_code = $1; +-- name: GetUserReferralsCount :one SELECT COUNT(*) -FROM referrals +FROM user_referrals + JOIN referral_codes ON referral_codes.id == referral_code_id WHERE referrer_id = $1; \ No newline at end of file diff --git a/gen/db/auth.sql.go b/gen/db/auth.sql.go index 1817514..7d8d59d 100644 --- a/gen/db/auth.sql.go +++ b/gen/db/auth.sql.go @@ -76,7 +76,7 @@ func (q *Queries) GetRefreshTokenByUserID(ctx context.Context, userID int64) (Re } const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one -SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by +SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended FROM users WHERE ( email = $1 @@ -112,8 +112,6 @@ func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPho &i.CompanyID, &i.SuspendedAt, &i.Suspended, - &i.ReferralCode, - &i.ReferredBy, ) return i, err } diff --git a/gen/db/cashier.sql.go b/gen/db/cashier.sql.go index c15f497..fc4a7f8 100644 --- a/gen/db/cashier.sql.go +++ b/gen/db/cashier.sql.go @@ -12,7 +12,7 @@ import ( ) const GetAllCashiers = `-- name: GetAllCashiers :many -SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by, +SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, branch_id, branches.name AS branch_name, branches.wallet_id AS branch_wallet, @@ -57,8 +57,6 @@ type GetAllCashiersRow struct { CompanyID pgtype.Int8 `json:"company_id"` SuspendedAt pgtype.Timestamptz `json:"suspended_at"` Suspended bool `json:"suspended"` - ReferralCode pgtype.Text `json:"referral_code"` - ReferredBy pgtype.Text `json:"referred_by"` BranchID int64 `json:"branch_id"` BranchName string `json:"branch_name"` BranchWallet int64 `json:"branch_wallet"` @@ -89,8 +87,6 @@ func (q *Queries) GetAllCashiers(ctx context.Context, arg GetAllCashiersParams) &i.CompanyID, &i.SuspendedAt, &i.Suspended, - &i.ReferralCode, - &i.ReferredBy, &i.BranchID, &i.BranchName, &i.BranchWallet, @@ -107,7 +103,7 @@ func (q *Queries) GetAllCashiers(ctx context.Context, arg GetAllCashiersParams) } const GetCashierByID = `-- name: GetCashierByID :one -SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by, +SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, branch_id, branches.name AS branch_name, branches.wallet_id AS branch_wallet, @@ -133,8 +129,6 @@ type GetCashierByIDRow struct { CompanyID pgtype.Int8 `json:"company_id"` SuspendedAt pgtype.Timestamptz `json:"suspended_at"` Suspended bool `json:"suspended"` - ReferralCode pgtype.Text `json:"referral_code"` - ReferredBy pgtype.Text `json:"referred_by"` BranchID int64 `json:"branch_id"` BranchName string `json:"branch_name"` BranchWallet int64 `json:"branch_wallet"` @@ -159,8 +153,6 @@ func (q *Queries) GetCashierByID(ctx context.Context, id int64) (GetCashierByIDR &i.CompanyID, &i.SuspendedAt, &i.Suspended, - &i.ReferralCode, - &i.ReferredBy, &i.BranchID, &i.BranchName, &i.BranchWallet, @@ -170,7 +162,7 @@ func (q *Queries) GetCashierByID(ctx context.Context, id int64) (GetCashierByIDR } const GetCashiersByBranch = `-- name: GetCashiersByBranch :many -SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by +SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended FROM branch_cashiers JOIN users ON branch_cashiers.user_id = users.id JOIN branches ON branches.id = branch_id @@ -201,8 +193,6 @@ func (q *Queries) GetCashiersByBranch(ctx context.Context, branchID int64) ([]Us &i.CompanyID, &i.SuspendedAt, &i.Suspended, - &i.ReferralCode, - &i.ReferredBy, ); err != nil { return nil, err } diff --git a/gen/db/models.go b/gen/db/models.go index e41c659..3af1538 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -5,56 +5,9 @@ package dbgen import ( - "database/sql/driver" - "fmt" - "github.com/jackc/pgx/v5/pgtype" ) -type Referralstatus string - -const ( - ReferralstatusPENDING Referralstatus = "PENDING" - ReferralstatusCOMPLETED Referralstatus = "COMPLETED" - ReferralstatusEXPIRED Referralstatus = "EXPIRED" - ReferralstatusCANCELLED Referralstatus = "CANCELLED" -) - -func (e *Referralstatus) Scan(src interface{}) error { - switch s := src.(type) { - case []byte: - *e = Referralstatus(s) - case string: - *e = Referralstatus(s) - default: - return fmt.Errorf("unsupported scan type for Referralstatus: %T", src) - } - return nil -} - -type NullReferralstatus struct { - Referralstatus Referralstatus `json:"referralstatus"` - Valid bool `json:"valid"` // Valid is true if Referralstatus is not NULL -} - -// Scan implements the Scanner interface. -func (ns *NullReferralstatus) Scan(value interface{}) error { - if value == nil { - ns.Referralstatus, ns.Valid = "", false - return nil - } - ns.Valid = true - return ns.Referralstatus.Scan(value) -} - -// Value implements the driver Valuer interface. -func (ns NullReferralstatus) Value() (driver.Value, error) { - if !ns.Valid { - return nil, nil - } - return string(ns.Referralstatus), nil -} - type Bank struct { ID int64 `json:"id"` Slug string `json:"slug"` @@ -538,31 +491,16 @@ type Otp struct { ExpiresAt pgtype.Timestamptz `json:"expires_at"` } -type Referral struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - ReferralCode string `json:"referral_code"` - ReferrerID int64 `json:"referrer_id"` - ReferredID pgtype.Int8 `json:"referred_id"` - Status Referralstatus `json:"status"` - RewardAmount pgtype.Numeric `json:"reward_amount"` - CashbackAmount pgtype.Numeric `json:"cashback_amount"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - ExpiresAt pgtype.Timestamptz `json:"expires_at"` -} - -type ReferralSetting struct { - ID int64 `json:"id"` - ReferralRewardAmount pgtype.Numeric `json:"referral_reward_amount"` - CashbackPercentage pgtype.Numeric `json:"cashback_percentage"` - BetReferralBonusPercentage pgtype.Numeric `json:"bet_referral_bonus_percentage"` - MaxReferrals int32 `json:"max_referrals"` - ExpiresAfterDays int32 `json:"expires_after_days"` - UpdatedBy string `json:"updated_by"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - Version int32 `json:"version"` +type ReferralCode struct { + ID int64 `json:"id"` + ReferralCode string `json:"referral_code"` + ReferrerID int64 `json:"referrer_id"` + CompanyID int64 `json:"company_id"` + IsActive bool `json:"is_active"` + NumberOfReferrals int64 `json:"number_of_referrals"` + RewardAmount int64 `json:"reward_amount"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } type RefreshToken struct { @@ -795,8 +733,6 @@ type User struct { CompanyID pgtype.Int8 `json:"company_id"` SuspendedAt pgtype.Timestamptz `json:"suspended_at"` Suspended bool `json:"suspended"` - ReferralCode pgtype.Text `json:"referral_code"` - ReferredBy pgtype.Text `json:"referred_by"` } type UserGameInteraction struct { @@ -809,6 +745,13 @@ type UserGameInteraction struct { CreatedAt pgtype.Timestamptz `json:"created_at"` } +type UserReferral struct { + ID int64 `json:"id"` + ReferredID int64 `json:"referred_id"` + ReferralCodeID int64 `json:"referral_code_id"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + type VirtualGame struct { ID int64 `json:"id"` GameID string `json:"game_id"` @@ -897,8 +840,6 @@ type Wallet struct { IsActive bool `json:"is_active"` CreatedAt pgtype.Timestamp `json:"created_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"` - BonusBalance pgtype.Numeric `json:"bonus_balance"` - CashBalance pgtype.Numeric `json:"cash_balance"` } type WalletThresholdNotification struct { diff --git a/gen/db/referal.sql.go b/gen/db/referal.sql.go index 6db003a..621692d 100644 --- a/gen/db/referal.sql.go +++ b/gen/db/referal.sql.go @@ -7,237 +7,138 @@ package dbgen import ( "context" - - "github.com/jackc/pgx/v5/pgtype" ) -const CreateReferral = `-- name: CreateReferral :one -INSERT INTO referrals ( +const CreateReferralCode = `-- name: CreateReferralCode :one +INSERT INTO referral_codes ( referral_code, referrer_id, company_id, - status, - reward_amount, - expires_at + number_of_referrals, + reward_amount ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING id, company_id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at +VALUES ($1, $2, $3, $4, $5) +RETURNING id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at ` -type CreateReferralParams struct { - ReferralCode string `json:"referral_code"` - ReferrerID int64 `json:"referrer_id"` - CompanyID int64 `json:"company_id"` - Status Referralstatus `json:"status"` - RewardAmount pgtype.Numeric `json:"reward_amount"` - ExpiresAt pgtype.Timestamptz `json:"expires_at"` +type CreateReferralCodeParams struct { + ReferralCode string `json:"referral_code"` + ReferrerID int64 `json:"referrer_id"` + CompanyID int64 `json:"company_id"` + NumberOfReferrals int64 `json:"number_of_referrals"` + RewardAmount int64 `json:"reward_amount"` } -func (q *Queries) CreateReferral(ctx context.Context, arg CreateReferralParams) (Referral, error) { - row := q.db.QueryRow(ctx, CreateReferral, +func (q *Queries) CreateReferralCode(ctx context.Context, arg CreateReferralCodeParams) (ReferralCode, error) { + row := q.db.QueryRow(ctx, CreateReferralCode, arg.ReferralCode, arg.ReferrerID, arg.CompanyID, - arg.Status, + arg.NumberOfReferrals, arg.RewardAmount, - arg.ExpiresAt, ) - var i Referral + var i ReferralCode err := row.Scan( &i.ID, - &i.CompanyID, &i.ReferralCode, &i.ReferrerID, - &i.ReferredID, - &i.Status, - &i.RewardAmount, - &i.CashbackAmount, - &i.CreatedAt, - &i.UpdatedAt, - &i.ExpiresAt, - ) - return i, err -} - -const CreateReferralSettings = `-- name: CreateReferralSettings :one -INSERT INTO referral_settings ( - referral_reward_amount, - cashback_percentage, - max_referrals, - bet_referral_bonus_percentage, - expires_after_days, - updated_by - ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING id, referral_reward_amount, cashback_percentage, bet_referral_bonus_percentage, max_referrals, expires_after_days, updated_by, created_at, updated_at, version -` - -type CreateReferralSettingsParams struct { - ReferralRewardAmount pgtype.Numeric `json:"referral_reward_amount"` - CashbackPercentage pgtype.Numeric `json:"cashback_percentage"` - MaxReferrals int32 `json:"max_referrals"` - BetReferralBonusPercentage pgtype.Numeric `json:"bet_referral_bonus_percentage"` - ExpiresAfterDays int32 `json:"expires_after_days"` - UpdatedBy string `json:"updated_by"` -} - -func (q *Queries) CreateReferralSettings(ctx context.Context, arg CreateReferralSettingsParams) (ReferralSetting, error) { - row := q.db.QueryRow(ctx, CreateReferralSettings, - arg.ReferralRewardAmount, - arg.CashbackPercentage, - arg.MaxReferrals, - arg.BetReferralBonusPercentage, - arg.ExpiresAfterDays, - arg.UpdatedBy, - ) - var i ReferralSetting - err := row.Scan( - &i.ID, - &i.ReferralRewardAmount, - &i.CashbackPercentage, - &i.BetReferralBonusPercentage, - &i.MaxReferrals, - &i.ExpiresAfterDays, - &i.UpdatedBy, - &i.CreatedAt, - &i.UpdatedAt, - &i.Version, - ) - return i, err -} - -const GetActiveReferralByReferrerID = `-- name: GetActiveReferralByReferrerID :one -SELECT id, company_id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at -FROM referrals -WHERE referrer_id = $1 - AND status = 'PENDING' -LIMIT 1 -` - -func (q *Queries) GetActiveReferralByReferrerID(ctx context.Context, referrerID int64) (Referral, error) { - row := q.db.QueryRow(ctx, GetActiveReferralByReferrerID, referrerID) - var i Referral - err := row.Scan( - &i.ID, &i.CompanyID, - &i.ReferralCode, - &i.ReferrerID, - &i.ReferredID, - &i.Status, + &i.IsActive, + &i.NumberOfReferrals, &i.RewardAmount, - &i.CashbackAmount, &i.CreatedAt, &i.UpdatedAt, - &i.ExpiresAt, ) return i, err } -const GetReferralByCode = `-- name: GetReferralByCode :one -SELECT id, company_id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at -FROM referrals +const CreateUserReferral = `-- name: CreateUserReferral :one +INSERT INTO user_referrals (referred_id, referral_code_id) +VALUES ($1, $2) +RETURNING id, referred_id, referral_code_id, created_at +` + +type CreateUserReferralParams struct { + ReferredID int64 `json:"referred_id"` + ReferralCodeID int64 `json:"referral_code_id"` +} + +func (q *Queries) CreateUserReferral(ctx context.Context, arg CreateUserReferralParams) (UserReferral, error) { + row := q.db.QueryRow(ctx, CreateUserReferral, arg.ReferredID, arg.ReferralCodeID) + var i UserReferral + err := row.Scan( + &i.ID, + &i.ReferredID, + &i.ReferralCodeID, + &i.CreatedAt, + ) + return i, err +} + +const GetReferralCode = `-- name: GetReferralCode :one +SELECT id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at +FROM referral_codes WHERE referral_code = $1 ` -func (q *Queries) GetReferralByCode(ctx context.Context, referralCode string) (Referral, error) { - row := q.db.QueryRow(ctx, GetReferralByCode, referralCode) - var i Referral +func (q *Queries) GetReferralCode(ctx context.Context, referralCode string) (ReferralCode, error) { + row := q.db.QueryRow(ctx, GetReferralCode, referralCode) + var i ReferralCode err := row.Scan( &i.ID, - &i.CompanyID, &i.ReferralCode, &i.ReferrerID, - &i.ReferredID, - &i.Status, + &i.CompanyID, + &i.IsActive, + &i.NumberOfReferrals, &i.RewardAmount, - &i.CashbackAmount, &i.CreatedAt, &i.UpdatedAt, - &i.ExpiresAt, ) return i, err } -const GetReferralByReferredID = `-- name: GetReferralByReferredID :one -SELECT id, company_id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at -FROM referrals -WHERE referred_id = $1 -LIMIT 1 -` - -func (q *Queries) GetReferralByReferredID(ctx context.Context, referredID pgtype.Int8) (Referral, error) { - row := q.db.QueryRow(ctx, GetReferralByReferredID, referredID) - var i Referral - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.ReferralCode, - &i.ReferrerID, - &i.ReferredID, - &i.Status, - &i.RewardAmount, - &i.CashbackAmount, - &i.CreatedAt, - &i.UpdatedAt, - &i.ExpiresAt, - ) - return i, err -} - -const GetReferralCountByID = `-- name: GetReferralCountByID :one -SELECT COUNT(*) -FROM referrals +const GetReferralCodeByUser = `-- name: GetReferralCodeByUser :many +SELECt id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at +FROM referral_codes WHERE referrer_id = $1 ` -func (q *Queries) GetReferralCountByID(ctx context.Context, referrerID int64) (int64, error) { - row := q.db.QueryRow(ctx, GetReferralCountByID, referrerID) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetReferralSettings = `-- name: GetReferralSettings :one -SELECT id, referral_reward_amount, cashback_percentage, bet_referral_bonus_percentage, max_referrals, expires_after_days, updated_by, created_at, updated_at, version -FROM referral_settings -LIMIT 1 -` - -func (q *Queries) GetReferralSettings(ctx context.Context) (ReferralSetting, error) { - row := q.db.QueryRow(ctx, GetReferralSettings) - var i ReferralSetting - err := row.Scan( - &i.ID, - &i.ReferralRewardAmount, - &i.CashbackPercentage, - &i.BetReferralBonusPercentage, - &i.MaxReferrals, - &i.ExpiresAfterDays, - &i.UpdatedBy, - &i.CreatedAt, - &i.UpdatedAt, - &i.Version, - ) - return i, err +func (q *Queries) GetReferralCodeByUser(ctx context.Context, referrerID int64) ([]ReferralCode, error) { + rows, err := q.db.Query(ctx, GetReferralCodeByUser, referrerID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ReferralCode + for rows.Next() { + var i ReferralCode + if err := rows.Scan( + &i.ID, + &i.ReferralCode, + &i.ReferrerID, + &i.CompanyID, + &i.IsActive, + &i.NumberOfReferrals, + &i.RewardAmount, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil } const GetReferralStats = `-- name: GetReferralStats :one SELECT COUNT(*) AS total_referrals, - COUNT( - CASE - WHEN status = 'COMPLETED' THEN 1 - END - ) AS completed_referrals, - COALESCE(SUM(reward_amount), 0) AS total_reward_earned, - COALESCE( - SUM( - CASE - WHEN status = 'PENDING' THEN reward_amount - END - ), - 0 - ) AS pending_rewards -FROM referrals + SUM(reward_amount) AS total_reward_earned +FROM user_referrals + JOIN referral_codes ON referral_codes.id == referral_code_id WHERE referrer_id = $1 AND company_id = $2 ` @@ -248,120 +149,106 @@ type GetReferralStatsParams struct { } type GetReferralStatsRow struct { - TotalReferrals int64 `json:"total_referrals"` - CompletedReferrals int64 `json:"completed_referrals"` - TotalRewardEarned interface{} `json:"total_reward_earned"` - PendingRewards interface{} `json:"pending_rewards"` + TotalReferrals int64 `json:"total_referrals"` + TotalRewardEarned int64 `json:"total_reward_earned"` } func (q *Queries) GetReferralStats(ctx context.Context, arg GetReferralStatsParams) (GetReferralStatsRow, error) { row := q.db.QueryRow(ctx, GetReferralStats, arg.ReferrerID, arg.CompanyID) var i GetReferralStatsRow - err := row.Scan( - &i.TotalReferrals, - &i.CompletedReferrals, - &i.TotalRewardEarned, - &i.PendingRewards, - ) + err := row.Scan(&i.TotalReferrals, &i.TotalRewardEarned) return i, err } -const UpdateReferral = `-- name: UpdateReferral :one -UPDATE referrals -SET referred_id = $2, - status = $3, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -RETURNING id, company_id, referral_code, referrer_id, referred_id, status, reward_amount, cashback_amount, created_at, updated_at, expires_at +const GetUserReferral = `-- name: GetUserReferral :one +SELECT id, referred_id, referral_code_id, created_at +FROM user_referrals +WHERE referred_id = $1 ` -type UpdateReferralParams struct { - ID int64 `json:"id"` - ReferredID pgtype.Int8 `json:"referred_id"` - Status Referralstatus `json:"status"` -} - -func (q *Queries) UpdateReferral(ctx context.Context, arg UpdateReferralParams) (Referral, error) { - row := q.db.QueryRow(ctx, UpdateReferral, arg.ID, arg.ReferredID, arg.Status) - var i Referral +func (q *Queries) GetUserReferral(ctx context.Context, referredID int64) (UserReferral, error) { + row := q.db.QueryRow(ctx, GetUserReferral, referredID) + var i UserReferral err := row.Scan( &i.ID, - &i.CompanyID, - &i.ReferralCode, - &i.ReferrerID, &i.ReferredID, - &i.Status, - &i.RewardAmount, - &i.CashbackAmount, + &i.ReferralCodeID, &i.CreatedAt, - &i.UpdatedAt, - &i.ExpiresAt, ) return i, err } +const GetUserReferralsByCode = `-- name: GetUserReferralsByCode :many +SELECT user_referrals.id, user_referrals.referred_id, user_referrals.referral_code_id, user_referrals.created_at +FROM user_referrals + JOIN referral_codes ON referral_codes.id == referral_code_id +WHERE referral_code = $1 +` + +func (q *Queries) GetUserReferralsByCode(ctx context.Context, referralCode string) ([]UserReferral, error) { + rows, err := q.db.Query(ctx, GetUserReferralsByCode, referralCode) + if err != nil { + return nil, err + } + defer rows.Close() + var items []UserReferral + for rows.Next() { + var i UserReferral + if err := rows.Scan( + &i.ID, + &i.ReferredID, + &i.ReferralCodeID, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const GetUserReferralsCount = `-- name: GetUserReferralsCount :one +SELECT COUNT(*) +FROM user_referrals + JOIN referral_codes ON referral_codes.id == referral_code_id +WHERE referrer_id = $1 +` + +func (q *Queries) GetUserReferralsCount(ctx context.Context, referrerID int64) (int64, error) { + row := q.db.QueryRow(ctx, GetUserReferralsCount, referrerID) + var count int64 + err := row.Scan(&count) + return count, err +} + const UpdateReferralCode = `-- name: UpdateReferralCode :exec -UPDATE users -SET referral_code = $2, +UPDATE referral_codes +SET is_active = $2, + referral_code = $3, + number_of_referrals = $4, + reward_amount = $5, updated_at = CURRENT_TIMESTAMP WHERE id = $1 ` type UpdateReferralCodeParams struct { - ID int64 `json:"id"` - ReferralCode pgtype.Text `json:"referral_code"` + ID int64 `json:"id"` + IsActive bool `json:"is_active"` + ReferralCode string `json:"referral_code"` + NumberOfReferrals int64 `json:"number_of_referrals"` + RewardAmount int64 `json:"reward_amount"` } func (q *Queries) UpdateReferralCode(ctx context.Context, arg UpdateReferralCodeParams) error { - _, err := q.db.Exec(ctx, UpdateReferralCode, arg.ID, arg.ReferralCode) + _, err := q.db.Exec(ctx, UpdateReferralCode, + arg.ID, + arg.IsActive, + arg.ReferralCode, + arg.NumberOfReferrals, + arg.RewardAmount, + ) return err } - -const UpdateReferralSettings = `-- name: UpdateReferralSettings :one -UPDATE referral_settings -SET referral_reward_amount = $2, - cashback_percentage = $3, - bet_referral_bonus_percentage = $4, - max_referrals = $5, - expires_after_days = $6, - updated_by = $7, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -RETURNING id, referral_reward_amount, cashback_percentage, bet_referral_bonus_percentage, max_referrals, expires_after_days, updated_by, created_at, updated_at, version -` - -type UpdateReferralSettingsParams struct { - ID int64 `json:"id"` - ReferralRewardAmount pgtype.Numeric `json:"referral_reward_amount"` - CashbackPercentage pgtype.Numeric `json:"cashback_percentage"` - BetReferralBonusPercentage pgtype.Numeric `json:"bet_referral_bonus_percentage"` - MaxReferrals int32 `json:"max_referrals"` - ExpiresAfterDays int32 `json:"expires_after_days"` - UpdatedBy string `json:"updated_by"` -} - -func (q *Queries) UpdateReferralSettings(ctx context.Context, arg UpdateReferralSettingsParams) (ReferralSetting, error) { - row := q.db.QueryRow(ctx, UpdateReferralSettings, - arg.ID, - arg.ReferralRewardAmount, - arg.CashbackPercentage, - arg.BetReferralBonusPercentage, - arg.MaxReferrals, - arg.ExpiresAfterDays, - arg.UpdatedBy, - ) - var i ReferralSetting - err := row.Scan( - &i.ID, - &i.ReferralRewardAmount, - &i.CashbackPercentage, - &i.BetReferralBonusPercentage, - &i.MaxReferrals, - &i.ExpiresAfterDays, - &i.UpdatedBy, - &i.CreatedAt, - &i.UpdatedAt, - &i.Version, - ) - return i, err -} diff --git a/gen/db/user.sql.go b/gen/db/user.sql.go index 43d9156..999f169 100644 --- a/gen/db/user.sql.go +++ b/gen/db/user.sql.go @@ -163,7 +163,7 @@ func (q *Queries) DeleteUser(ctx context.Context, id int64) error { } const GetAdminByCompanyID = `-- name: GetAdminByCompanyID :one -SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by +SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended FROM companies JOIN users ON companies.admin_id = users.id where companies.id = $1 @@ -187,8 +187,6 @@ func (q *Queries) GetAdminByCompanyID(ctx context.Context, id int64) (User, erro &i.CompanyID, &i.SuspendedAt, &i.Suspended, - &i.ReferralCode, - &i.ReferredBy, ) return i, err } @@ -388,7 +386,7 @@ func (q *Queries) GetUserByEmail(ctx context.Context, arg GetUserByEmailParams) } const GetUserByID = `-- name: GetUserByID :one -SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by +SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended FROM users WHERE id = $1 ` @@ -411,8 +409,6 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) { &i.CompanyID, &i.SuspendedAt, &i.Suspended, - &i.ReferralCode, - &i.ReferredBy, ) return i, err } diff --git a/gen/db/wallet.sql.go b/gen/db/wallet.sql.go index 7e3eb7f..fcde631 100644 --- a/gen/db/wallet.sql.go +++ b/gen/db/wallet.sql.go @@ -50,7 +50,7 @@ INSERT INTO wallets ( type ) VALUES ($1, $2, $3, $4, $5) -RETURNING id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance +RETURNING id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at ` type CreateWalletParams struct { @@ -82,8 +82,6 @@ func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wal &i.IsActive, &i.CreatedAt, &i.UpdatedAt, - &i.BonusBalance, - &i.CashBalance, ) return i, err } @@ -188,7 +186,7 @@ func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDet } const GetAllWallets = `-- name: GetAllWallets :many -SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance +SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at FROM wallets ` @@ -213,8 +211,6 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) { &i.IsActive, &i.CreatedAt, &i.UpdatedAt, - &i.BonusBalance, - &i.CashBalance, ); err != nil { return nil, err } @@ -319,7 +315,7 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (Cust } const GetWalletByID = `-- name: GetWalletByID :one -SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance +SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at FROM wallets WHERE id = $1 ` @@ -339,14 +335,12 @@ func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) { &i.IsActive, &i.CreatedAt, &i.UpdatedAt, - &i.BonusBalance, - &i.CashBalance, ) return i, err } const GetWalletByUserID = `-- name: GetWalletByUserID :many -SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at, bonus_balance, cash_balance +SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at FROM wallets WHERE user_id = $1 ` @@ -372,8 +366,6 @@ func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet &i.IsActive, &i.CreatedAt, &i.UpdatedAt, - &i.BonusBalance, - &i.CashBalance, ); err != nil { return nil, err } diff --git a/internal/domain/referal.go b/internal/domain/referal.go index b8f61bf..5e384f1 100644 --- a/internal/domain/referal.go +++ b/internal/domain/referal.go @@ -1,74 +1,138 @@ package domain import ( - "database/sql/driver" - "fmt" "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" ) -type ReferralStatus string - -const ( - ReferralPending ReferralStatus = "PENDING" - ReferralCompleted ReferralStatus = "COMPLETED" - ReferralExpired ReferralStatus = "EXPIRED" - ReferralCancelled ReferralStatus = "CANCELLED" -) - -func (rs *ReferralStatus) Scan(src interface{}) error { - switch s := src.(type) { - case []byte: - *rs = ReferralStatus(s) - case string: - *rs = ReferralStatus(s) - default: - return fmt.Errorf("unsupported scan type for ReferralStatus: %T", src) - } - return nil +type ReferralCode struct { + ID int64 + ReferrerID int64 + ReferralCode string + CompanyID int64 + NumberOfReferrals int64 + RewardAmount Currency + CreatedAt time.Time + UpdatedAt time.Time } -func (rs ReferralStatus) Value() (driver.Value, error) { - return string(rs), nil +type CreateReferralCode struct { + ReferrerID int64 + ReferralCode string + CompanyID int64 + NumberOfReferrals int64 + RewardAmount Currency +} + +type UserReferral struct { + ReferredID int64 + ReferralCodeID int64 +} + +type CreateUserReferrals struct { + ReferredID int64 + ReferralCodeID int64 +} + +type UpdateReferralCode struct { + ID int64 + IsActive bool + ReferralCode string + RewardAmount Currency + NumberOfReferrals int64 } type ReferralStats struct { - TotalReferrals int - CompletedReferrals int - TotalRewardEarned float64 - PendingRewards float64 + TotalReferrals int64 + TotalRewardEarned Currency } -type ReferralSettings struct { - ID int64 - ReferralRewardAmount float64 - CashbackPercentage float64 - BetReferralBonusPercentage float64 - MaxReferrals int32 - ExpiresAfterDays int32 - UpdatedBy string - CreatedAt time.Time - UpdatedAt time.Time - Version int32 +// type ReferralSettings struct { +// ID int64 +// ReferralRewardAmount float64 +// CashbackPercentage float64 +// BetReferralBonusPercentage float64 +// MaxReferrals int32 +// ExpiresAfterDays int32 +// UpdatedBy string +// CreatedAt time.Time +// UpdatedAt time.Time +// Version int32 +// } + +// type ReferralSettingsReq struct { +// ReferralRewardAmount float64 `json:"referral_reward_amount" validate:"required"` +// CashbackPercentage float64 `json:"cashback_percentage" validate:"required"` +// MaxReferrals int32 `json:"max_referrals" validate:"required"` +// UpdatedBy string `json:"updated_by" validate:"required"` +// } + +func ConvertCreateReferralCode(code CreateReferralCode) dbgen.CreateReferralCodeParams { + return dbgen.CreateReferralCodeParams{ + ReferralCode: code.ReferralCode, + ReferrerID: code.ReferrerID, + CompanyID: code.CompanyID, + NumberOfReferrals: code.NumberOfReferrals, + RewardAmount: int64(code.RewardAmount), + } } -type ReferralSettingsReq struct { - ReferralRewardAmount float64 `json:"referral_reward_amount" validate:"required"` - CashbackPercentage float64 `json:"cashback_percentage" validate:"required"` - MaxReferrals int32 `json:"max_referrals" validate:"required"` - ExpiresAfterDays int32 `json:"expires_afterdays" validate:"required"` - UpdatedBy string `json:"updated_by" validate:"required"` +func ConvertDBReferralCode(code dbgen.ReferralCode) ReferralCode { + return ReferralCode{ + ID: code.ID, + ReferrerID: code.ReferrerID, + ReferralCode: code.ReferralCode, + CompanyID: code.CompanyID, + CreatedAt: code.CreatedAt.Time, + UpdatedAt: code.UpdatedAt.Time, + } } -type Referral struct { - ID int64 - ReferralCode string - ReferrerID int64 - CompanyID int64 - ReferredID *int64 - Status ReferralStatus - RewardAmount float64 - CashbackAmount float64 - CreatedAt time.Time - UpdatedAt time.Time - ExpiresAt time.Time +func ConvertDBReferralCodes(codes []dbgen.ReferralCode) []ReferralCode { + result := make([]ReferralCode, len(codes)) + for i, code := range codes { + result[i] = ConvertDBReferralCode(code) + } + return result +} + +func ConvertCreateUserReferral(referral CreateUserReferrals) dbgen.CreateUserReferralParams { + return dbgen.CreateUserReferralParams{ + ReferredID: referral.ReferredID, + ReferralCodeID: referral.ReferralCodeID, + } +} + +func ConvertDBUserReferral(referral dbgen.UserReferral) UserReferral { + return UserReferral{ + ReferredID: referral.ReferredID, + ReferralCodeID: referral.ReferralCodeID, + } +} + +func ConvertDBUserReferrals(referrals []dbgen.UserReferral) []UserReferral { + result := make([]UserReferral, len(referrals)) + for i, referral := range referrals { + result[i] = ConvertDBUserReferral(referral) + } + + return result +} + +func ConvertUpdateReferralCode(referralCode UpdateReferralCode) dbgen.UpdateReferralCodeParams { + return dbgen.UpdateReferralCodeParams{ + ID: referralCode.ID, + IsActive: referralCode.IsActive, + ReferralCode: referralCode.ReferralCode, + NumberOfReferrals: referralCode.NumberOfReferrals, + RewardAmount: int64(referralCode.RewardAmount), + } +} + +func ConvertDBReferralStats(stats dbgen.GetReferralStatsRow) ReferralStats { + return ReferralStats{ + TotalReferrals: stats.TotalReferrals, + TotalRewardEarned: Currency(stats.TotalRewardEarned), + } } diff --git a/internal/domain/setting_list.go b/internal/domain/setting_list.go index 52ae2a4..96ffe1a 100644 --- a/internal/domain/setting_list.go +++ b/internal/domain/setting_list.go @@ -24,6 +24,9 @@ type SettingList struct { AmountForBetReferral Currency `json:"amount_for_bet_referral"` CashbackAmountCap Currency `json:"cashback_amount_cap"` DefaultWinningLimit int64 `json:"default_winning_limit"` + ReferralRewardAmount Currency `json:"referral_reward_amount"` + CashbackPercentage float32 `json:"cashback_percentage"` + DefaultMaxReferrals int64 `json:"default_max_referrals"` } type SettingListRes struct { @@ -35,6 +38,9 @@ type SettingListRes struct { AmountForBetReferral float32 `json:"amount_for_bet_referral"` CashbackAmountCap float32 `json:"cashback_amount_cap"` DefaultWinningLimit int64 `json:"default_winning_limit"` + ReferralRewardAmount float32 `json:"referral_reward_amount"` + CashbackPercentage float32 `json:"cashback_percentage"` + DefaultMaxReferrals int64 `json:"default_max_referrals"` } func ConvertSettingListRes(settings SettingList) SettingListRes { @@ -47,6 +53,9 @@ func ConvertSettingListRes(settings SettingList) SettingListRes { AmountForBetReferral: settings.AmountForBetReferral.Float32(), CashbackAmountCap: settings.CashbackAmountCap.Float32(), DefaultWinningLimit: settings.DefaultWinningLimit, + ReferralRewardAmount: settings.ReferralRewardAmount.Float32(), + CashbackPercentage: settings.CashbackPercentage, + DefaultMaxReferrals: settings.DefaultMaxReferrals, } } @@ -59,6 +68,9 @@ type SaveSettingListReq struct { AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"` CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"` DefaultWinningLimit *int64 `json:"default_winning_limit,omitempty"` + ReferralRewardAmount *float32 `json:"referral_reward_amount"` + CashbackPercentage *float32 `json:"cashback_percentage"` + DefaultMaxReferrals *int64 `json:"default_max_referrals"` } type ValidSettingList struct { @@ -70,6 +82,9 @@ type ValidSettingList struct { AmountForBetReferral ValidCurrency CashbackAmountCap ValidCurrency DefaultWinningLimit ValidInt64 + ReferralRewardAmount ValidCurrency + CashbackPercentage ValidFloat32 + DefaultMaxReferrals ValidInt64 } func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList { @@ -82,6 +97,9 @@ func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList { AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral), CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap), DefaultWinningLimit: ConvertInt64Ptr(settings.DefaultWinningLimit), + ReferralRewardAmount: ConvertFloat32PtrToCurrency(settings.ReferralRewardAmount), + CashbackPercentage: ConvertFloat32Ptr(settings.CashbackPercentage), + DefaultMaxReferrals: ConvertInt64Ptr(settings.DefaultMaxReferrals), } } @@ -90,12 +108,15 @@ func (vsl *ValidSettingList) ToSettingList() SettingList { return SettingList{ SMSProvider: SMSProvider(vsl.SMSProvider.Value), MaxNumberOfOutcomes: vsl.MaxNumberOfOutcomes.Value, - BetAmountLimit: Currency(vsl.BetAmountLimit.Value), + BetAmountLimit: vsl.BetAmountLimit.Value, DailyTicketPerIP: vsl.DailyTicketPerIP.Value, - TotalWinningLimit: Currency(vsl.TotalWinningLimit.Value), - AmountForBetReferral: Currency(vsl.AmountForBetReferral.Value), - CashbackAmountCap: Currency(vsl.CashbackAmountCap.Value), + TotalWinningLimit: vsl.TotalWinningLimit.Value, + AmountForBetReferral: vsl.AmountForBetReferral.Value, + CashbackAmountCap: vsl.CashbackAmountCap.Value, DefaultWinningLimit: vsl.DefaultWinningLimit.Value, + ReferralRewardAmount: vsl.ReferralRewardAmount.Value, + CashbackPercentage: vsl.CashbackPercentage.Value, + DefaultMaxReferrals: vsl.DefaultMaxReferrals.Value, } } @@ -112,6 +133,7 @@ func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 { "max_number_of_outcomes": &vsl.MaxNumberOfOutcomes, "daily_ticket_limit": &vsl.DailyTicketPerIP, "default_winning_limit": &vsl.DefaultWinningLimit, + "default_max_referrals": &vsl.DefaultMaxReferrals, } } @@ -121,6 +143,7 @@ func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency "total_winnings_limit": &vsl.TotalWinningLimit, "amount_for_bet_referral": &vsl.AmountForBetReferral, "cashback_amount_cap": &vsl.CashbackAmountCap, + "referral_reward_amount": &vsl.ReferralRewardAmount, } } @@ -135,13 +158,18 @@ func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool { } func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 { - return map[string]*ValidFloat32{} + return map[string]*ValidFloat32{ + "cashback_percentage": &vsl.CashbackPercentage, + } } func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime { return map[string]*ValidTime{} } + +// Setting Functions + func (vsl *ValidSettingList) GetTotalSettings() int { return len(vsl.GetInt64SettingsMap()) + len(vsl.GetCurrencySettingsMap()) + diff --git a/internal/repository/referal.go b/internal/repository/referal.go index 8b44c8a..cda22e2 100644 --- a/internal/repository/referal.go +++ b/internal/repository/referal.go @@ -2,28 +2,21 @@ package repository import ( "context" - "database/sql" - "errors" - "fmt" - "strconv" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" ) type ReferralRepository interface { - CreateReferral(ctx context.Context, referral *domain.Referral) error - GetReferralByCode(ctx context.Context, code string) (*domain.Referral, error) - UpdateReferral(ctx context.Context, referral *domain.Referral) error - GetReferralStats(ctx context.Context, userID int64, companyID int64) (*domain.ReferralStats, error) - GetSettings(ctx context.Context) (*domain.ReferralSettings, error) - UpdateSettings(ctx context.Context, settings *domain.ReferralSettings) error - CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error - GetReferralByReferredID(ctx context.Context, referredID int64) (*domain.Referral, error) - GetReferralCountByID(ctx context.Context, referrerID int64) (int64, error) - GetActiveReferralByReferrerID(ctx context.Context, referrerID int64) (*domain.Referral, error) - UpdateUserReferalCode(ctx context.Context, codedata domain.UpdateUserReferalCode) error + CreateReferralCode(ctx context.Context, referralCode domain.CreateReferralCode) (domain.ReferralCode, error) + CreateUserReferral(ctx context.Context, referral domain.CreateUserReferrals) (domain.UserReferral, error) + GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) + GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) + UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error + GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) + GetUserReferral(ctx context.Context, referredID int64) (domain.UserReferral, error) + GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) + GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) } type ReferralRepo struct { @@ -34,252 +27,159 @@ func NewReferralRepository(store *Store) ReferralRepository { return &ReferralRepo{store: store} } -func (r *ReferralRepo) UpdateUserReferalCode(ctx context.Context, codedata domain.UpdateUserReferalCode) error { - params := dbgen.UpdateReferralCodeParams{ - ID: codedata.UserID, - ReferralCode: pgtype.Text{ - String: codedata.Code, - Valid: true, - }, - } +func (r *ReferralRepo) CreateReferralCode(ctx context.Context, referralCode domain.CreateReferralCode) (domain.ReferralCode, error) { + newReferralCode, err := r.store.queries.CreateReferralCode(ctx, domain.ConvertCreateReferralCode(referralCode)) - return r.store.queries.UpdateReferralCode(ctx, params) + if err != nil { + return domain.ReferralCode{}, err + } + return domain.ConvertDBReferralCode(newReferralCode), nil } -func (r *ReferralRepo) CreateReferral(ctx context.Context, referral *domain.Referral) error { - rewardAmount := pgtype.Numeric{} - if err := rewardAmount.Scan(strconv.Itoa(int(referral.RewardAmount))); err != nil { +func (r *ReferralRepo) CreateUserReferral(ctx context.Context, referral domain.CreateUserReferrals) (domain.UserReferral, error) { + newReferral, err := r.store.queries.CreateUserReferral(ctx, domain.ConvertCreateUserReferral(referral)) + + if err != nil { + return domain.UserReferral{}, err + } + + return domain.ConvertDBUserReferral(newReferral), nil +} + +func (r *ReferralRepo) GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) { + codes, err := r.store.queries.GetReferralCodeByUser(ctx, userID) + + if err != nil { + return nil, err + } + + return domain.ConvertDBReferralCodes(codes), nil +} + +func (r *ReferralRepo) GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) { + referralCode, err := r.store.queries.GetReferralCode(ctx, code) + + if err != nil { + return domain.ReferralCode{}, err + } + + return domain.ConvertDBReferralCode(referralCode), nil + +} +func (r *ReferralRepo) UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error { + err := r.store.queries.UpdateReferralCode(ctx, domain.ConvertUpdateReferralCode(referral)) + + if err != nil { return err } - params := dbgen.CreateReferralParams{ - ReferralCode: referral.ReferralCode, - ReferrerID: referral.ReferrerID, - Status: dbgen.Referralstatus(referral.Status), - RewardAmount: rewardAmount, - ExpiresAt: pgtype.Timestamptz{Time: referral.ExpiresAt, Valid: true}, - CompanyID: referral.CompanyID, - } - - _, err := r.store.queries.CreateReferral(ctx, params) - return err + return nil } -func (r *ReferralRepo) GetReferralByCode(ctx context.Context, code string) (*domain.Referral, error) { - dbReferral, err := r.store.queries.GetReferralByCode(ctx, code) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - return nil, err - } - return r.mapToDomainReferral(&dbReferral), nil -} - -func (r *ReferralRepo) UpdateReferral(ctx context.Context, referral *domain.Referral) error { - var referredID pgtype.Int8 - if referral.ReferredID != nil { - referredID = pgtype.Int8{Int64: *referral.ReferredID, Valid: true} - } - - params := dbgen.UpdateReferralParams{ - ID: referral.ID, - ReferredID: referredID, - Status: dbgen.Referralstatus(referral.Status), - } - - _, err := r.store.queries.UpdateReferral(ctx, params) - return err -} - -func (r *ReferralRepo) GetReferralStats(ctx context.Context, userID int64, companyID int64) (*domain.ReferralStats, error) { +func (r *ReferralRepo) GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) { stats, err := r.store.queries.GetReferralStats(ctx, dbgen.GetReferralStatsParams{ ReferrerID: userID, CompanyID: companyID, }) + if err != nil { + return domain.ReferralStats{}, err + } + + return domain.ConvertDBReferralStats(stats), nil +} + +func (r *ReferralRepo) GetUserReferral(ctx context.Context, referredID int64) (domain.UserReferral, error) { + dbReferral, err := r.store.queries.GetUserReferral(ctx, referredID) + if err != nil { + return domain.UserReferral{}, err + } + return domain.ConvertDBUserReferral(dbReferral), nil +} + +func (r *ReferralRepo) GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) { + dbReferrals, err := r.store.queries.GetUserReferralsByCode(ctx, code) + if err != nil { return nil, err } - return &domain.ReferralStats{ - TotalReferrals: int(stats.TotalReferrals), - CompletedReferrals: int(stats.CompletedReferrals), - TotalRewardEarned: stats.TotalRewardEarned.(float64), - PendingRewards: stats.PendingRewards.(float64), - }, nil + return domain.ConvertDBUserReferrals(dbReferrals), nil } -func (r *ReferralRepo) GetSettings(ctx context.Context) (*domain.ReferralSettings, error) { - settings, err := r.store.queries.GetReferralSettings(ctx) +func (r *ReferralRepo) GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) { + count, err := r.store.queries.GetUserReferralsCount(ctx, referrerID) if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - return nil, err - } - return r.mapToDomainSettings(&settings), nil -} - -func (r *ReferralRepo) UpdateSettings(ctx context.Context, settings *domain.ReferralSettings) error { - rewardAmount := pgtype.Numeric{} - if err := rewardAmount.Scan(settings.ReferralRewardAmount); err != nil { - return err - } - - cashbackPercentage := pgtype.Numeric{} - if err := cashbackPercentage.Scan(settings.CashbackPercentage); err != nil { - return err - } - - betReferralBonusPercentage := pgtype.Numeric{} - if err := betReferralBonusPercentage.Scan(settings.BetReferralBonusPercentage); err != nil { - return err - } - - params := dbgen.UpdateReferralSettingsParams{ - ID: settings.ID, - ReferralRewardAmount: rewardAmount, - CashbackPercentage: cashbackPercentage, - BetReferralBonusPercentage: betReferralBonusPercentage, // New field - MaxReferrals: settings.MaxReferrals, - ExpiresAfterDays: settings.ExpiresAfterDays, - UpdatedBy: settings.UpdatedBy, - } - - _, err := r.store.queries.UpdateReferralSettings(ctx, params) - return err -} - -func (r *ReferralRepo) CreateSettings(ctx context.Context, settings *domain.ReferralSettings) error { - rewardAmount := pgtype.Numeric{} - if err := rewardAmount.Scan(fmt.Sprintf("%f", settings.ReferralRewardAmount)); err != nil { - return err - } - - cashbackPercentage := pgtype.Numeric{} - if err := cashbackPercentage.Scan(fmt.Sprintf("%f", settings.CashbackPercentage)); err != nil { - return err - } - - betReferralBonusPercentage := pgtype.Numeric{} - if err := betReferralBonusPercentage.Scan(fmt.Sprintf("%f", settings.BetReferralBonusPercentage)); err != nil { - return err - } - - params := dbgen.CreateReferralSettingsParams{ - ReferralRewardAmount: rewardAmount, - CashbackPercentage: cashbackPercentage, - BetReferralBonusPercentage: betReferralBonusPercentage, // New field - MaxReferrals: settings.MaxReferrals, - ExpiresAfterDays: settings.ExpiresAfterDays, - UpdatedBy: settings.UpdatedBy, - } - - _, err := r.store.queries.CreateReferralSettings(ctx, params) - return err -} - -func (r *ReferralRepo) GetReferralByReferredID(ctx context.Context, referredID int64) (*domain.Referral, error) { - dbReferral, err := r.store.queries.GetReferralByReferredID(ctx, pgtype.Int8{Int64: referredID, Valid: true}) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - return nil, err - } - return r.mapToDomainReferral(&dbReferral), nil -} - -func (r *ReferralRepo) GetReferralCountByID(ctx context.Context, referrerID int64) (int64, error) { - count, err := r.store.queries.GetReferralCountByID(ctx, referrerID) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return 0, nil - } return 0, err } - return count, nil } -func (r *ReferralRepo) GetActiveReferralByReferrerID(ctx context.Context, referrerID int64) (*domain.Referral, error) { - referral, err := r.store.queries.GetActiveReferralByReferrerID(ctx, referrerID) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return &domain.Referral{}, nil - } - return &domain.Referral{}, err - } +// func (r *ReferralRepo) mapToDomainReferral(dbRef *dbgen.Referral) *domain.Referral { +// var referredID *int64 +// if dbRef.ReferredID.Valid { +// referredID = &dbRef.ReferredID.Int64 +// } - return r.mapToDomainReferral(&referral), nil -} +// rewardAmount := 0.0 +// if dbRef.RewardAmount.Valid { +// if f8, err := dbRef.RewardAmount.Float64Value(); err == nil { +// rewardAmount = f8.Float64 +// } +// } -func (r *ReferralRepo) mapToDomainReferral(dbRef *dbgen.Referral) *domain.Referral { - var referredID *int64 - if dbRef.ReferredID.Valid { - referredID = &dbRef.ReferredID.Int64 - } +// cashbackAmount := 0.0 +// if dbRef.CashbackAmount.Valid { +// if f8, err := dbRef.CashbackAmount.Float64Value(); err == nil { +// cashbackAmount = f8.Float64 +// } +// } - rewardAmount := 0.0 - if dbRef.RewardAmount.Valid { - if f8, err := dbRef.RewardAmount.Float64Value(); err == nil { - rewardAmount = f8.Float64 - } - } +// return &domain.Referral{ +// ID: dbRef.ID, +// ReferralCode: dbRef.ReferralCode, +// ReferrerID: dbRef.ReferrerID, +// ReferredID: referredID, +// Status: domain.ReferralStatus(dbRef.Status), +// RewardAmount: rewardAmount, +// CashbackAmount: cashbackAmount, +// CreatedAt: dbRef.CreatedAt.Time, +// UpdatedAt: dbRef.UpdatedAt.Time, +// ExpiresAt: dbRef.ExpiresAt.Time, +// } +// } - cashbackAmount := 0.0 - if dbRef.CashbackAmount.Valid { - if f8, err := dbRef.CashbackAmount.Float64Value(); err == nil { - cashbackAmount = f8.Float64 - } - } +// func (r *ReferralRepo) mapToDomainSettings(dbSettings *dbgen.ReferralSetting) *domain.ReferralSettings { +// rewardAmount := 0.0 +// if dbSettings.ReferralRewardAmount.Valid { +// if f8, err := dbSettings.ReferralRewardAmount.Float64Value(); err == nil { +// rewardAmount = f8.Float64 +// } +// } - return &domain.Referral{ - ID: dbRef.ID, - ReferralCode: dbRef.ReferralCode, - ReferrerID: dbRef.ReferrerID, - ReferredID: referredID, - Status: domain.ReferralStatus(dbRef.Status), - RewardAmount: rewardAmount, - CashbackAmount: cashbackAmount, - CreatedAt: dbRef.CreatedAt.Time, - UpdatedAt: dbRef.UpdatedAt.Time, - ExpiresAt: dbRef.ExpiresAt.Time, - } -} +// cashbackPercentage := 0.0 +// if dbSettings.CashbackPercentage.Valid { +// if f8, err := dbSettings.CashbackPercentage.Float64Value(); err == nil { +// cashbackPercentage = f8.Float64 +// } +// } -func (r *ReferralRepo) mapToDomainSettings(dbSettings *dbgen.ReferralSetting) *domain.ReferralSettings { - rewardAmount := 0.0 - if dbSettings.ReferralRewardAmount.Valid { - if f8, err := dbSettings.ReferralRewardAmount.Float64Value(); err == nil { - rewardAmount = f8.Float64 - } - } +// betReferralBonusPercentage := 0.0 +// if dbSettings.BetReferralBonusPercentage.Valid { +// if f8, err := dbSettings.BetReferralBonusPercentage.Float64Value(); err == nil { +// betReferralBonusPercentage = f8.Float64 +// } +// } - cashbackPercentage := 0.0 - if dbSettings.CashbackPercentage.Valid { - if f8, err := dbSettings.CashbackPercentage.Float64Value(); err == nil { - cashbackPercentage = f8.Float64 - } - } - - betReferralBonusPercentage := 0.0 - if dbSettings.BetReferralBonusPercentage.Valid { - if f8, err := dbSettings.BetReferralBonusPercentage.Float64Value(); err == nil { - betReferralBonusPercentage = f8.Float64 - } - } - - return &domain.ReferralSettings{ - ID: dbSettings.ID, - ReferralRewardAmount: rewardAmount, - CashbackPercentage: cashbackPercentage, - BetReferralBonusPercentage: betReferralBonusPercentage, // New field - MaxReferrals: dbSettings.MaxReferrals, - ExpiresAfterDays: dbSettings.ExpiresAfterDays, - UpdatedBy: dbSettings.UpdatedBy, - CreatedAt: dbSettings.CreatedAt.Time, - UpdatedAt: dbSettings.UpdatedAt.Time, - Version: dbSettings.Version, - } -} +// return &domain.ReferralSettings{ +// ID: dbSettings.ID, +// ReferralRewardAmount: rewardAmount, +// CashbackPercentage: cashbackPercentage, +// BetReferralBonusPercentage: betReferralBonusPercentage, // New field +// MaxReferrals: dbSettings.MaxReferrals, +// ExpiresAfterDays: dbSettings.ExpiresAfterDays, +// UpdatedBy: dbSettings.UpdatedBy, +// CreatedAt: dbSettings.CreatedAt.Time, +// UpdatedAt: dbSettings.UpdatedAt.Time, +// Version: dbSettings.Version, +// } +// } diff --git a/internal/repository/user.go b/internal/repository/user.go index 0c72347..50c6593 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -9,6 +9,7 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) @@ -73,7 +74,7 @@ func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int6 func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) { user, err := s.queries.GetUserByID(ctx, id) if err != nil { - if errors.Is(err, sql.ErrNoRows) { + if errors.Is(err, pgx.ErrNoRows) { return domain.User{}, domain.ErrUserNotFound } return domain.User{}, err diff --git a/internal/services/referal/port.go b/internal/services/referal/port.go index 1b2278a..6930c0e 100644 --- a/internal/services/referal/port.go +++ b/internal/services/referal/port.go @@ -13,8 +13,5 @@ type ReferralStore interface { ProcessDepositBonus(ctx context.Context, userPhone string, amount float64) error ProcessBetReferral(ctx context.Context, userId int64, betAmount float64) error GetReferralStats(ctx context.Context, userID int64, companyID int64) (*domain.ReferralStats, error) - CreateReferralSettings(ctx context.Context, req domain.ReferralSettingsReq) error - UpdateReferralSettings(ctx context.Context, settings *domain.ReferralSettings) error - GetReferralSettings(ctx context.Context) (*domain.ReferralSettings, error) GetReferralCountByID(ctx context.Context, referrerID int64) (int64, error) } diff --git a/internal/services/referal/service.go b/internal/services/referal/service.go index 159d494..a3b2bf5 100644 --- a/internal/services/referal/service.go +++ b/internal/services/referal/service.go @@ -7,38 +7,38 @@ import ( "errors" "fmt" "log/slog" - "strconv" - "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" ) type Service struct { - repo repository.ReferralRepository - walletSvc wallet.Service - store *repository.Store - config *config.Config - logger *slog.Logger + repo repository.ReferralRepository + walletSvc wallet.Service + settingSvc settings.Service + config *config.Config + logger *slog.Logger } -func New(repo repository.ReferralRepository, walletSvc wallet.Service, store *repository.Store, cfg *config.Config, logger *slog.Logger) *Service { +func New(repo repository.ReferralRepository, walletSvc wallet.Service, settingSvc settings.Service, cfg *config.Config, logger *slog.Logger) *Service { return &Service{ - repo: repo, - walletSvc: walletSvc, - store: store, - config: cfg, - logger: logger, + repo: repo, + walletSvc: walletSvc, + settingSvc: settingSvc, + config: cfg, + logger: logger, } } var ( - ErrInvalidReferral = errors.New("invalid or expired referral") - ErrInvalidReferralSignup = errors.New("referral requires phone signup") - ErrUserNotFound = errors.New("user not found") - ErrNoReferralFound = errors.New("no referral found for this user") + ErrInvalidReferral = errors.New("invalid or expired referral") + ErrUserNotFound = errors.New("user not found") + ErrNoReferralFound = errors.New("no referral found for this user") + ErrUserAlreadyHasReferralCode = errors.New("user already has an active referral code") + ErrMaxReferralCountLimitReached = errors.New("referral count limit has been reached") ) func (s *Service) GenerateReferralCode() (string, error) { @@ -52,274 +52,100 @@ func (s *Service) GenerateReferralCode() (string, error) { return code, nil } -func (s *Service) CreateReferral(ctx context.Context, userID int64, companyID int64) error { +func (s *Service) CreateReferralCode(ctx context.Context, userID int64, companyID int64) (domain.ReferralCode, error) { + + settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID) + if err != nil { + s.logger.Error("Failed to fetch settings", "error", err) + return domain.ReferralCode{}, err + } + s.logger.Info("Creating referral code for user", "userID", userID) // check if user already has an active referral code - referral, err := s.repo.GetActiveReferralByReferrerID(ctx, userID) + referralCodes, err := s.repo.GetReferralCodesByUser(ctx, userID) if err != nil { s.logger.Error("Failed to check if user alredy has active referral code", "error", err) - return err + return domain.ReferralCode{}, err } - if referral != nil && referral.Status == domain.ReferralPending && referral.ExpiresAt.After(time.Now()) { + if referralCodes != nil { s.logger.Error("user already has an active referral code", "error", err) - return err - } - - settings, err := s.GetReferralSettings(ctx) - if err != nil || settings == nil { - s.logger.Error("Failed to fetch referral settings", "error", err) - return err - } - - // check referral count limit - referralCount, err := s.GetReferralCountByID(ctx, userID) - if err != nil { - s.logger.Error("Failed to get referral count", "userID", userID, "error", err) - return err - } - - fmt.Println("referralCount: ", referralCount) - if referralCount == int64(settings.MaxReferrals) { - s.logger.Error("referral count limit has been reached", "referralCount", referralCount, "error", err) - return err + return domain.ReferralCode{}, ErrUserAlreadyHasReferralCode } code, err := s.GenerateReferralCode() if err != nil { s.logger.Error("Failed to generate referral code", "error", err) - return err + return domain.ReferralCode{}, err } - var rewardAmount float64 = settings.ReferralRewardAmount - var expireDuration time.Time = time.Now().Add(time.Duration((24 * settings.ExpiresAfterDays)) * time.Hour) + newReferralCode, err := s.repo.CreateReferralCode(ctx, domain.CreateReferralCode{ + ReferrerID: userID, + ReferralCode: code, + CompanyID: companyID, + NumberOfReferrals: settingsList.DefaultMaxReferrals, + RewardAmount: settingsList.ReferralRewardAmount, + }) - if err := s.repo.CreateReferral(ctx, &domain.Referral{ - ReferralCode: code, - ReferrerID: userID, - Status: domain.ReferralPending, - RewardAmount: rewardAmount, - ExpiresAt: expireDuration, - CompanyID: companyID, - }); err != nil { - return err + if err != nil { + return domain.ReferralCode{}, err } - return nil + return newReferralCode, nil } -func (s *Service) ProcessReferral(ctx context.Context, referredPhone, referralCode string, companyID int64) error { - s.logger.Info("Processing referral", "referredPhone", referredPhone, "referralCode", referralCode) +func (s *Service) ProcessReferral(ctx context.Context, referredID int64, referralCode string, companyID int64) error { + s.logger.Info("Processing referral", "referralCode", referralCode) - referral, err := s.repo.GetReferralByCode(ctx, referralCode) - if err != nil || referral == nil { + referral, err := s.repo.GetReferralCode(ctx, referralCode) + + if err != nil { s.logger.Error("Failed to get referral by code", "referralCode", referralCode, "error", err) return err } - if referral.Status != domain.ReferralPending || referral.ExpiresAt.Before(time.Now()) { - s.logger.Warn("Invalid or expired referral", "referralCode", referralCode, "status", referral.Status) - return ErrInvalidReferral - } - - user, err := s.store.GetUserByPhone(ctx, referredPhone, domain.ValidInt64{ - Value: companyID, - Valid: true, - }) - if err != nil { - if errors.Is(err, domain.ErrUserNotFound) { - s.logger.Warn("User not found for referral", "referredPhone", referredPhone) - return ErrUserNotFound - } - s.logger.Error("Failed to get user by phone", "referredPhone", referredPhone, "error", err) - return err - } - if !user.PhoneVerified { - s.logger.Warn("Phone not verified for referral", "referredPhone", referredPhone) - return ErrInvalidReferralSignup - } - - referral.ReferredID = &user.ID - referral.Status = domain.ReferralCompleted - referral.UpdatedAt = time.Now() - - if err := s.repo.UpdateReferral(ctx, referral); err != nil { - s.logger.Error("Failed to update referral", "referralCode", referralCode, "error", err) - return err - } - - wallets, err := s.store.GetCustomerWallet(ctx, referral.ReferrerID) + wallets, err := s.walletSvc.GetCustomerWallet(ctx, referral.ReferrerID) if err != nil { s.logger.Error("Failed to get referrer wallets", "referrerId", referral.ReferrerID, "error", err) return err } _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, - domain.ToCurrency(float32(referral.RewardAmount)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to static wallet because of referral ID %v", referral.RewardAmount, referral.ReferrerID), + referral.RewardAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, + fmt.Sprintf("Added %v to static wallet due to %v referral code being used", referral.RewardAmount, referral.ReferralCode), ) if err != nil { - s.logger.Error("Failed to add referral reward to static wallet", "walletID", wallets.StaticID, "referrer phone number", referredPhone, "error", err) + s.logger.Error("Failed to add referral reward to static wallet", "walletID", wallets.StaticID, "error", err) return err } - s.logger.Info("Referral processed successfully", "referredPhone", referredPhone, "referralCode", referralCode, "rewardAmount", referral.RewardAmount) + _, err = s.repo.CreateUserReferral(ctx, domain.CreateUserReferrals{ + ReferredID: referredID, + ReferralCodeID: referral.ID, + }) + + if err != nil { + s.logger.Error("Failed to add referral reward to static wallet", "referredID", referredID, "error", err) + return err + } + + s.logger.Info("Referral processed successfully", "referralCode", referralCode, "rewardAmount", referral.RewardAmount) return nil } -func (s *Service) ProcessDepositBonus(ctx context.Context, userPhone string, amount float64) error { - s.logger.Info("Processing deposit bonus", "userPhone", userPhone, "amount", amount) - - settings, err := s.repo.GetSettings(ctx) - if err != nil { - s.logger.Error("Failed to get referral settings", "error", err) - return err - } - - userID, err := strconv.ParseInt(userPhone, 10, 64) - if err != nil { - s.logger.Error("Invalid phone number format", "userPhone", userPhone, "error", err) - return errors.New("invalid phone number format") - } - - wallets, err := s.walletSvc.GetWalletsByUser(ctx, userID) - if err != nil { - s.logger.Error("Failed to get wallets for user", "userID", userID, "error", err) - return err - } - if len(wallets) == 0 { - s.logger.Error("User has no wallet", "userID", userID) - return errors.New("user has no wallet") - } - - walletID := wallets[0].ID - bonus := amount * (settings.CashbackPercentage / 100) - currentBonus := float64(wallets[0].Balance) - _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBonus+bonus)), domain.ValidInt64{}, - domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to static wallet because of Deposit Cashback Bonus %d", currentBonus+bonus, bonus)) - if err != nil { - s.logger.Error("Failed to add deposit bonus to wallet", "walletID", walletID, "userID", userID, "bonus", bonus, "error", err) - return err - } - - s.logger.Info("Deposit bonus processed successfully", "userPhone", userPhone, "bonus", bonus) - return nil -} - -func (s *Service) ProcessBetReferral(ctx context.Context, userId int64, betAmount float64) error { - s.logger.Info("Processing bet referral", "userID", userId, "betAmount", betAmount) - - settings, err := s.repo.GetSettings(ctx) - if err != nil { - s.logger.Error("Failed to get referral settings", "error", err) - return err - } - - referral, err := s.repo.GetReferralByReferredID(ctx, userId) - if err != nil { - s.logger.Error("Failed to get referral by referred ID", "userId", userId, "error", err) - return err - } - if referral == nil || referral.Status != domain.ReferralCompleted { - s.logger.Warn("No valid referral found", "userId", userId, "status", referral.Status) - return ErrNoReferralFound - } - - - wallets, err := s.walletSvc.GetWalletsByUser(ctx, referral.ReferrerID) - if err != nil { - s.logger.Error("Failed to get wallets for referrer", "referrerID", referral.ReferrerID, "error", err) - return err - } - if len(wallets) == 0 { - s.logger.Error("Referrer has no wallet", "referrerID", referral.ReferrerID) - return errors.New("referrer has no wallet") - } - - bonusPercentage := settings.BetReferralBonusPercentage - if bonusPercentage == 0 { - bonusPercentage = 5.0 - s.logger.Debug("Using default bet referral bonus percentage", "percentage", bonusPercentage) - } - bonus := betAmount * (bonusPercentage / 100) - - walletID := wallets[0].ID - currentBalance := float64(wallets[0].Balance) - _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBalance+bonus)), domain.ValidInt64{}, - domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to static wallet because of bet referral", referral.RewardAmount)) - if err != nil { - s.logger.Error("Failed to add bet referral bonus to wallet", "walletID", walletID, "referrerID", referral.ReferrerID, "bonus", bonus, "error", err) - return err - } - - s.logger.Info("Bet referral processed successfully", "referrer ID", referral.ReferrerID, "referrerID", referral.ReferrerID, "bonus", bonus) - return nil -} - -func (s *Service) GetReferralStats(ctx context.Context, userID int64, companyID int64) (*domain.ReferralStats, error) { - s.logger.Info("Fetching referral stats", "userID", userID) - +func (s *Service) GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) { stats, err := s.repo.GetReferralStats(ctx, userID, companyID) if err != nil { s.logger.Error("Failed to get referral stats", "userID", userID, "error", err) - return nil, err + return domain.ReferralStats{}, err } s.logger.Info("Referral stats retrieved successfully", "userID", userID, "totalReferrals", stats.TotalReferrals) return stats, nil } -func (s *Service) CreateReferralSettings(ctx context.Context, req domain.ReferralSettingsReq) error { - s.logger.Info("Creating referral setting") - - if err := s.repo.CreateSettings(ctx, &domain.ReferralSettings{ - ReferralRewardAmount: req.ReferralRewardAmount, - CashbackPercentage: req.CashbackPercentage, - MaxReferrals: req.MaxReferrals, - ExpiresAfterDays: req.ExpiresAfterDays, - UpdatedBy: req.UpdatedBy, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - }); err != nil { - s.logger.Error("Failed to create referral setting", "error", err) - return err - } - - s.logger.Info("Referral setting created succesfully") - return nil -} - -func (s *Service) UpdateReferralSettings(ctx context.Context, settings *domain.ReferralSettings) error { - s.logger.Info("Updating referral settings", "settingsID", settings.ID) - - settings.UpdatedAt = time.Now() - err := s.repo.UpdateSettings(ctx, settings) - if err != nil { - s.logger.Error("Failed to update referral settings", "settingsID", settings.ID, "error", err) - return err - } - - s.logger.Info("Referral settings updated successfully", "settingsID", settings.ID) - return nil -} - -func (s *Service) GetReferralSettings(ctx context.Context) (*domain.ReferralSettings, error) { - s.logger.Info("Fetching referral settings") - - settings, err := s.repo.GetSettings(ctx) - if err != nil { - s.logger.Error("Failed to get referral settings", "error", err) - return nil, err - } - - s.logger.Info("Referral settings retrieved successfully", "settings", settings) - return settings, nil -} - -func (s *Service) GetReferralCountByID(ctx context.Context, referrerID int64) (int64, error) { - count, err := s.repo.GetReferralCountByID(ctx, referrerID) +func (s *Service) GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) { + count, err := s.repo.GetUserReferralCount(ctx, referrerID) if err != nil { s.logger.Error("Failed to get referral count", "userID", referrerID, "error", err) return 0, err @@ -327,3 +153,88 @@ func (s *Service) GetReferralCountByID(ctx context.Context, referrerID int64) (i return count, nil } + + + + + + +// func (s *Service) ProcessDepositBonus(ctx context.Context, userID int64, amount float32, companyID int64) error { +// settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID) +// if err != nil { +// s.logger.Error("Failed to fetch settings", "error", err) +// return err +// } + +// s.logger.Info("Processing deposit bonus", "amount", amount) + +// customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, userID) +// if err != nil { +// s.logger.Error("Failed to get wallets for user", "userID", userID, "error", err) +// return err +// } + +// bonus := amount * settingsList.CashbackPercentage + +// _, err = s.walletSvc.AddToWallet(ctx, customerWallet.StaticID, domain.ToCurrency(bonus), domain.ValidInt64{}, +// domain.TRANSFER_DIRECT, domain.PaymentDetails{}, +// fmt.Sprintf("Added to bonus wallet because of Deposit Cashback Bonus %d", bonus)) +// if err != nil { +// s.logger.Error("Failed to add deposit bonus to wallet", "staticWalletID", customerWallet.StaticID, "userID", userID, "bonus", bonus, "error", err) +// return err +// } + +// s.logger.Info("Deposit bonus processed successfully", "bonus", bonus) +// return nil +// } + +// func (s *Service) ProcessBetReferral(ctx context.Context, userId int64, betAmount float64) error { +// s.logger.Info("Processing bet referral", "userID", userId, "betAmount", betAmount) + +// settings, err := s.repo.GetSettings(ctx) +// if err != nil { +// s.logger.Error("Failed to get referral settings", "error", err) +// return err +// } + +// referral, err := s.repo.GetReferralByReferredID(ctx, userId) +// if err != nil { +// s.logger.Error("Failed to get referral by referred ID", "userId", userId, "error", err) +// return err +// } +// if referral == nil || referral.Status != domain.ReferralCompleted { +// s.logger.Warn("No valid referral found", "userId", userId, "status", referral.Status) +// return ErrNoReferralFound +// } + +// wallets, err := s.walletSvc.GetWalletsByUser(ctx, referral.ReferrerID) +// if err != nil { +// s.logger.Error("Failed to get wallets for referrer", "referrerID", referral.ReferrerID, "error", err) +// return err +// } +// if len(wallets) == 0 { +// s.logger.Error("Referrer has no wallet", "referrerID", referral.ReferrerID) +// return errors.New("referrer has no wallet") +// } + +// bonusPercentage := settings.BetReferralBonusPercentage +// if bonusPercentage == 0 { +// bonusPercentage = 5.0 +// s.logger.Debug("Using default bet referral bonus percentage", "percentage", bonusPercentage) +// } +// bonus := betAmount * (bonusPercentage / 100) + +// walletID := wallets[0].ID +// currentBalance := float64(wallets[0].Balance) +// _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBalance+bonus)), domain.ValidInt64{}, +// domain.TRANSFER_DIRECT, domain.PaymentDetails{}, +// fmt.Sprintf("Added %v to static wallet because of bet referral", referral.RewardAmount)) +// if err != nil { +// s.logger.Error("Failed to add bet referral bonus to wallet", "walletID", walletID, "referrerID", referral.ReferrerID, "bonus", bonus, "error", err) +// return err +// } + +// s.logger.Info("Bet referral processed successfully", "referrer ID", referral.ReferrerID, "referrerID", referral.ReferrerID, "bonus", bonus) +// return nil +// } + diff --git a/internal/services/result/service.go b/internal/services/result/service.go index 6e5075e..4ae5bf3 100644 --- a/internal/services/result/service.go +++ b/internal/services/result/service.go @@ -567,9 +567,9 @@ The System`, greeting, counts.StatusEndedCount, totalBets) if counts.StatusNotFinishedCount > 0 { partsPlain = append(partsPlain, - fmt.Sprintf("- %d Unresolved Events (%d Bets)", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets)) + fmt.Sprintf("- %d Incomplete Events (%d Bets)", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets)) partsHTML = append(partsHTML, - fmt.Sprintf("