fix: refactoring referral

This commit is contained in:
Samuel Tariku 2025-09-08 17:05:09 +03:00
parent 9900113e33
commit e229ac911e
21 changed files with 744 additions and 1080 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {
type ReferralCode 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"`
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"`
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 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 {

View File

@ -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 {
type CreateReferralCodeParams 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"`
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(
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.ReferralRewardAmount,
&i.CashbackPercentage,
&i.BetReferralBonusPercentage,
&i.MaxReferrals,
&i.ExpiresAfterDays,
&i.UpdatedBy,
&i.ReferralCode,
&i.ReferrerID,
&i.CompanyID,
&i.IsActive,
&i.NumberOfReferrals,
&i.RewardAmount,
&i.CreatedAt,
&i.UpdatedAt,
&i.Version,
)
return i, err
); 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
`
@ -249,119 +150,105 @@ 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"`
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"`
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
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
}
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 {
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
}
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 domain.ReferralCode{}, err
}
return domain.ConvertDBReferralCode(newReferralCode), 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 r.mapToDomainReferral(&dbReferral), nil
return domain.ConvertDBReferralCodes(codes), 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}
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
}
params := dbgen.UpdateReferralParams{
ID: referral.ID,
ReferredID: referredID,
Status: dbgen.Referralstatus(referral.Status),
}
return domain.ConvertDBReferralCode(referralCode), nil
_, err := r.store.queries.UpdateReferral(ctx, params)
}
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
}
return nil
}
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,
// }
// }

View File

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

View File

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

View File

@ -7,28 +7,27 @@ 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
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,
settingSvc: settingSvc,
config: cfg,
logger: logger,
}
@ -36,9 +35,10 @@ func New(repo repository.ReferralRepository, walletSvc wallet.Service, store *re
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")
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)
if err := s.repo.CreateReferral(ctx, &domain.Referral{
ReferralCode: code,
newReferralCode, err := s.repo.CreateReferralCode(ctx, domain.CreateReferralCode{
ReferrerID: userID,
Status: domain.ReferralPending,
RewardAmount: rewardAmount,
ExpiresAt: expireDuration,
ReferralCode: code,
CompanyID: companyID,
}); err != nil {
return err
NumberOfReferrals: settingsList.DefaultMaxReferrals,
RewardAmount: settingsList.ReferralRewardAmount,
})
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
// }

View File

@ -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("<li><strong>%d Unresolved Events</strong> (%d Bets)</li>", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
fmt.Sprintf("<li><strong>%d Incomplete Events</strong> (%d Bets)</li>", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets))
}
if counts.StatusToBeFixedCount > 0 {
partsPlain = append(partsPlain,

View File

@ -56,7 +56,7 @@ type App struct {
cfg *config.Config
logger *slog.Logger
NotidicationStore *notificationservice.Service
referralSvc referralservice.ReferralStore
referralSvc *referralservice.Service
bonusSvc *bonus.Service
port int
settingSvc *settings.Service
@ -107,7 +107,7 @@ func NewApp(
prematchSvc *odds.ServiceImpl,
eventSvc event.Service,
leagueSvc league.Service,
referralSvc referralservice.ReferralStore,
referralSvc *referralservice.Service,
bonusSvc *bonus.Service,
virtualGameSvc virtualgameservice.VirtualGameService,
aleaVirtualGameService alea.AleaVirtualGameService,

View File

@ -26,32 +26,32 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
spec string
task func()
}{
{
spec: "0 0 * * * *", // Every 1 hour
task: func() {
mongoLogger.Info("Began fetching upcoming events cron task")
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
mongoLogger.Error("Failed to fetch upcoming events",
zap.Error(err),
)
} else {
mongoLogger.Info("Completed fetching upcoming events without errors")
}
},
},
{
spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
task: func() {
mongoLogger.Info("Began fetching non live odds cron task")
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
mongoLogger.Error("Failed to fetch non live odds",
zap.Error(err),
)
} else {
mongoLogger.Info("Completed fetching non live odds without errors")
}
},
},
// {
// spec: "0 0 * * * *", // Every 1 hour
// task: func() {
// mongoLogger.Info("Began fetching upcoming events cron task")
// if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
// mongoLogger.Error("Failed to fetch upcoming events",
// zap.Error(err),
// )
// } else {
// mongoLogger.Info("Completed fetching upcoming events without errors")
// }
// },
// },
// {
// spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
// task: func() {
// mongoLogger.Info("Began fetching non live odds cron task")
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
// mongoLogger.Error("Failed to fetch non live odds",
// zap.Error(err),
// )
// } else {
// mongoLogger.Info("Completed fetching non live odds without errors")
// }
// },
// },
{
spec: "0 */5 * * * *", // Every 5 Minutes
task: func() {
@ -78,19 +78,19 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
}
},
},
{
spec: "0 0 0 * * *", // Every Day
task: func() {
mongoLogger.Info("Began Send daily result notification cron task")
if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-24*time.Hour)); err != nil {
mongoLogger.Error("Failed to process result",
zap.Error(err),
)
} else {
mongoLogger.Info("Completed sending daily result notification without errors")
}
},
},
// {
// spec: "0 0 0 * * *", // Every Day
// task: func() {
// mongoLogger.Info("Began Send daily result notification cron task")
// if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-24*time.Hour)); err != nil {
// mongoLogger.Error("Failed to process result",
// zap.Error(err),
// )
// } else {
// mongoLogger.Info("Completed sending daily result notification without errors")
// }
// },
// },
}
for _, job := range schedule {
@ -154,10 +154,10 @@ func SetupReportandVirtualGameCronJobs(
spec string
period string
}{
{
spec: "*/60 * * * * *", // Every 1 minute for testing
period: "test",
},
// {
// spec: "*/60 * * * * *", // Every 1 minute for testing
// period: "test",
// },
{
spec: "0 0 0 * * *", // Daily at midnight
period: "daily",

View File

@ -48,7 +48,7 @@ type Handler struct {
settingSvc *settings.Service
notificationSvc *notificationservice.Service
userSvc *user.Service
referralSvc referralservice.ReferralStore
referralSvc *referralservice.Service
bonusSvc *bonus.Service
reportSvc report.ReportStore
chapaSvc *chapa.Service
@ -87,7 +87,7 @@ func New(
reportSvc report.ReportStore,
chapaSvc *chapa.Service,
walletSvc *wallet.Service,
referralSvc referralservice.ReferralStore,
referralSvc *referralservice.Service,
bonusSvc *bonus.Service,
virtualGameSvc virtualgameservice.VirtualGameService,
aleaVirtualGameSvc alea.AleaVirtualGameService,

View File

@ -11,6 +11,7 @@ import (
)
func (h *Handler) CreateReferralCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
@ -36,6 +37,7 @@ func (h *Handler) CreateReferralCode(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create referral")
}
fmt.Printf("Successfully created referral!")
return response.WriteJSON(c, fiber.StatusOK, "Referral created successfully", nil, nil)
}