integration issues

This commit is contained in:
Samuel Tariku 2025-05-16 22:38:10 +03:00
parent 95fb33c9d4
commit 4c6fb73342
30 changed files with 1213 additions and 202 deletions

View File

@ -234,12 +234,17 @@ CREATE TABLE companies (
wallet_id BIGINT NOT NULL wallet_id BIGINT NOT NULL
); );
-- Views -- Views
CREATE VIEW companies_with_wallets AS CREATE VIEW companies_details AS
SELECT companies.*, SELECT companies.*,
wallets.balance, wallets.balance,
wallets.is_active wallets.is_active,
users.first_name AS admin_first_name,
users.last_name AS admin_last_name,
users.phone_number AS admin_phone_number
FROM companies FROM companies
JOIN wallets ON wallets.id = companies.wallet_id; JOIN wallets ON wallets.id = companies.wallet_id
JOIN users ON users.id = companies.admin_id;
;
CREATE VIEW branch_details AS CREATE VIEW branch_details AS
SELECT branches.*, SELECT branches.*,
CONCAT(users.first_name, ' ', users.last_name) AS manager_name, CONCAT(users.first_name, ' ', users.last_name) AS manager_name,
@ -290,11 +295,11 @@ ALTER TABLE branch_operations
ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERENCES supported_operations(id) ON DELETE CASCADE, ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERENCES supported_operations(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_branch_operations_branches FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE; ADD CONSTRAINT fk_branch_operations_branches FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE;
ALTER TABLE branch_cashiers ALTER TABLE branch_cashiers
ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(id), ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches(id); ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE;
ALTER TABLE companies ALTER TABLE companies
ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id), ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id),
ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id); ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE;
----------------------------------------------seed data------------------------------------------------------------- ----------------------------------------------seed data-------------------------------------------------------------
-------------------------------------- DO NOT USE IN PRODUCTION------------------------------------------------- -------------------------------------- DO NOT USE IN PRODUCTION-------------------------------------------------
CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE EXTENSION IF NOT EXISTS pgcrypto;
@ -344,7 +349,7 @@ VALUES (
'Test', 'Test',
'Admin', 'Admin',
'test.admin@gmail.com', 'test.admin@gmail.com',
'0911111111', '0988554466',
crypt('password123', gen_salt('bf'))::bytea, crypt('password123', gen_salt('bf'))::bytea,
'admin', 'admin',
TRUE, TRUE,
@ -400,7 +405,7 @@ VALUES (
'Kirubel', 'Kirubel',
'Kibru', 'Kibru',
'kirubeljkl679 @gmail.com', 'kirubeljkl679 @gmail.com',
'0911111111', '0911554486',
crypt('password@123', gen_salt('bf'))::bytea, crypt('password@123', gen_salt('bf'))::bytea,
'super_admin', 'super_admin',
TRUE, TRUE,
@ -412,8 +417,7 @@ VALUES (
); );
INSERT INTO supported_operations (name, description) INSERT INTO supported_operations (name, description)
VALUES ('SportBook', 'Sportbook operations'), VALUES ('SportBook', 'Sportbook operations'),
('Virtual', 'Virtual operations'), ('Virtual', 'Virtual operations');
('GameZone', 'GameZone operations');
INSERT INTO wallets ( INSERT INTO wallets (
balance, balance,
is_withdraw, is_withdraw,
@ -434,3 +438,53 @@ VALUES (
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP CURRENT_TIMESTAMP
); );
INSERT INTO companies (
name,
admin_id,
wallet_id
)
values (
'Test Company',
2,
1
);
INSERT INTO wallets (
balance,
is_withdraw,
is_bettable,
is_transferable,
user_id,
is_active,
created_at,
updated_at
)
VALUES (
10000,
TRUE,
TRUE,
TRUE,
2,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
INSERT INTO branches (
name,
location,
wallet_id,
branch_manager_id,
company_id,
is_self_owned,
created_at,
updated_at
)
values (
'Test Branch',
'Addis Ababa',
2,
2,
1,
TRUE,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);

View File

@ -8,14 +8,14 @@ VALUES ($1, $2, $3)
RETURNING *; RETURNING *;
-- name: GetAllCompanies :many -- name: GetAllCompanies :many
SELECT * SELECT *
FROM companies_with_wallets; FROM companies_details;
-- name: GetCompanyByID :one -- name: GetCompanyByID :one
SELECT * SELECT *
FROM companies_with_wallets FROM companies_details
WHERE id = $1; WHERE id = $1;
-- name: SearchCompanyByName :many -- name: SearchCompanyByName :many
SELECT * SELECT *
FROM companies_with_wallets FROM companies_details
WHERE name ILIKE '%' || $1 || '%'; WHERE name ILIKE '%' || $1 || '%';
-- name: UpdateCompany :one -- name: UpdateCompany :one
UPDATE companies UPDATE companies

View File

@ -805,6 +805,53 @@ const docTemplate = `{
} }
} }
}, },
"/branch/{id}/cashier": {
"get": {
"description": "Gets branch cashiers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branch cashiers",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.GetCashierRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/branch/{id}/operation": { "/branch/{id}/operation": {
"get": { "get": {
"description": "Gets branch operations", "description": "Gets branch operations",
@ -2756,6 +2803,50 @@ const docTemplate = `{
} }
} }
}, },
"/user/delete/{id}": {
"delete": {
"description": "Delete a user by their ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Delete user by ID",
"parameters": [
{
"type": "integer",
"description": "User ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/user/profile": { "/user/profile": {
"get": { "get": {
"security": [ "security": [
@ -3052,7 +3143,7 @@ const docTemplate = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/response.APIResponse" "$ref": "#/definitions/handlers.UserProfileRes"
} }
}, },
"400": { "400": {
@ -3076,6 +3167,52 @@ const docTemplate = `{
} }
} }
}, },
"/user/suspend": {
"post": {
"description": "Suspend or unsuspend a user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Suspend or unsuspend a user",
"parameters": [
{
"description": "Suspend or unsuspend a user",
"name": "updateUserSuspend",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.UpdateUserSuspendReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.UpdateUserSuspendRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/user/wallet": { "/user/wallet": {
"get": { "get": {
"security": [ "security": [
@ -3601,9 +3738,11 @@ const docTemplate = `{
1, 1,
2, 2,
3, 3,
4 4,
5
], ],
"x-enum-comments": { "x-enum-comments": {
"OUTCOME_STATUS_ERROR": "Half Win and Half Given Back",
"OUTCOME_STATUS_HALF": "Half Win and Half Given Back", "OUTCOME_STATUS_HALF": "Half Win and Half Given Back",
"OUTCOME_STATUS_VOID": "Give Back" "OUTCOME_STATUS_VOID": "Give Back"
}, },
@ -3612,7 +3751,8 @@ const docTemplate = `{
"OUTCOME_STATUS_WIN", "OUTCOME_STATUS_WIN",
"OUTCOME_STATUS_LOSS", "OUTCOME_STATUS_LOSS",
"OUTCOME_STATUS_VOID", "OUTCOME_STATUS_VOID",
"OUTCOME_STATUS_HALF" "OUTCOME_STATUS_HALF",
"OUTCOME_STATUS_ERROR"
] ]
}, },
"domain.PaymentOption": { "domain.PaymentOption": {
@ -3660,10 +3800,18 @@ const docTemplate = `{
}, },
"domain.RandomBetReq": { "domain.RandomBetReq": {
"type": "object", "type": "object",
"required": [
"branch_id",
"number_of_bets"
],
"properties": { "properties": {
"branch_id": { "branch_id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
},
"number_of_bets": {
"type": "integer",
"example": 1
} }
} }
}, },
@ -4383,6 +4531,50 @@ const docTemplate = `{
} }
} }
}, },
"handlers.GetCashierRes": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"email": {
"type": "string"
},
"email_verified": {
"type": "boolean"
},
"first_name": {
"type": "string"
},
"id": {
"type": "integer"
},
"last_login": {
"type": "string"
},
"last_name": {
"type": "string"
},
"phone_number": {
"type": "string"
},
"phone_verified": {
"type": "boolean"
},
"role": {
"$ref": "#/definitions/domain.Role"
},
"suspended": {
"type": "boolean"
},
"suspended_at": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"handlers.ManagersRes": { "handlers.ManagersRes": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -4733,6 +4925,34 @@ const docTemplate = `{
} }
} }
}, },
"handlers.UpdateUserSuspendReq": {
"type": "object",
"required": [
"suspended",
"user_id"
],
"properties": {
"suspended": {
"type": "boolean",
"example": true
},
"user_id": {
"type": "integer",
"example": 123
}
}
},
"handlers.UpdateUserSuspendRes": {
"type": "object",
"properties": {
"suspended": {
"type": "boolean"
},
"user_id": {
"type": "integer"
}
}
},
"handlers.UpdateWalletActiveReq": { "handlers.UpdateWalletActiveReq": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -797,6 +797,53 @@
} }
} }
}, },
"/branch/{id}/cashier": {
"get": {
"description": "Gets branch cashiers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"branch"
],
"summary": "Gets branch cashiers",
"parameters": [
{
"type": "integer",
"description": "Branch ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.GetCashierRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/branch/{id}/operation": { "/branch/{id}/operation": {
"get": { "get": {
"description": "Gets branch operations", "description": "Gets branch operations",
@ -2748,6 +2795,50 @@
} }
} }
}, },
"/user/delete/{id}": {
"delete": {
"description": "Delete a user by their ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Delete user by ID",
"parameters": [
{
"type": "integer",
"description": "User ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/user/profile": { "/user/profile": {
"get": { "get": {
"security": [ "security": [
@ -3044,7 +3135,7 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/response.APIResponse" "$ref": "#/definitions/handlers.UserProfileRes"
} }
}, },
"400": { "400": {
@ -3068,6 +3159,52 @@
} }
} }
}, },
"/user/suspend": {
"post": {
"description": "Suspend or unsuspend a user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Suspend or unsuspend a user",
"parameters": [
{
"description": "Suspend or unsuspend a user",
"name": "updateUserSuspend",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.UpdateUserSuspendReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.UpdateUserSuspendRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/user/wallet": { "/user/wallet": {
"get": { "get": {
"security": [ "security": [
@ -3593,9 +3730,11 @@
1, 1,
2, 2,
3, 3,
4 4,
5
], ],
"x-enum-comments": { "x-enum-comments": {
"OUTCOME_STATUS_ERROR": "Half Win and Half Given Back",
"OUTCOME_STATUS_HALF": "Half Win and Half Given Back", "OUTCOME_STATUS_HALF": "Half Win and Half Given Back",
"OUTCOME_STATUS_VOID": "Give Back" "OUTCOME_STATUS_VOID": "Give Back"
}, },
@ -3604,7 +3743,8 @@
"OUTCOME_STATUS_WIN", "OUTCOME_STATUS_WIN",
"OUTCOME_STATUS_LOSS", "OUTCOME_STATUS_LOSS",
"OUTCOME_STATUS_VOID", "OUTCOME_STATUS_VOID",
"OUTCOME_STATUS_HALF" "OUTCOME_STATUS_HALF",
"OUTCOME_STATUS_ERROR"
] ]
}, },
"domain.PaymentOption": { "domain.PaymentOption": {
@ -3652,10 +3792,18 @@
}, },
"domain.RandomBetReq": { "domain.RandomBetReq": {
"type": "object", "type": "object",
"required": [
"branch_id",
"number_of_bets"
],
"properties": { "properties": {
"branch_id": { "branch_id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
},
"number_of_bets": {
"type": "integer",
"example": 1
} }
} }
}, },
@ -4375,6 +4523,50 @@
} }
} }
}, },
"handlers.GetCashierRes": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"email": {
"type": "string"
},
"email_verified": {
"type": "boolean"
},
"first_name": {
"type": "string"
},
"id": {
"type": "integer"
},
"last_login": {
"type": "string"
},
"last_name": {
"type": "string"
},
"phone_number": {
"type": "string"
},
"phone_verified": {
"type": "boolean"
},
"role": {
"$ref": "#/definitions/domain.Role"
},
"suspended": {
"type": "boolean"
},
"suspended_at": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"handlers.ManagersRes": { "handlers.ManagersRes": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -4725,6 +4917,34 @@
} }
} }
}, },
"handlers.UpdateUserSuspendReq": {
"type": "object",
"required": [
"suspended",
"user_id"
],
"properties": {
"suspended": {
"type": "boolean",
"example": true
},
"user_id": {
"type": "integer",
"example": 123
}
}
},
"handlers.UpdateUserSuspendRes": {
"type": "object",
"properties": {
"suspended": {
"type": "boolean"
},
"user_id": {
"type": "integer"
}
}
},
"handlers.UpdateWalletActiveReq": { "handlers.UpdateWalletActiveReq": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -165,8 +165,10 @@ definitions:
- 2 - 2
- 3 - 3
- 4 - 4
- 5
type: integer type: integer
x-enum-comments: x-enum-comments:
OUTCOME_STATUS_ERROR: Half Win and Half Given Back
OUTCOME_STATUS_HALF: Half Win and Half Given Back OUTCOME_STATUS_HALF: Half Win and Half Given Back
OUTCOME_STATUS_VOID: Give Back OUTCOME_STATUS_VOID: Give Back
x-enum-varnames: x-enum-varnames:
@ -175,6 +177,7 @@ definitions:
- OUTCOME_STATUS_LOSS - OUTCOME_STATUS_LOSS
- OUTCOME_STATUS_VOID - OUTCOME_STATUS_VOID
- OUTCOME_STATUS_HALF - OUTCOME_STATUS_HALF
- OUTCOME_STATUS_ERROR
domain.PaymentOption: domain.PaymentOption:
enum: enum:
- 0 - 0
@ -211,6 +214,12 @@ definitions:
branch_id: branch_id:
example: 1 example: 1
type: integer type: integer
number_of_bets:
example: 1
type: integer
required:
- branch_id
- number_of_bets
type: object type: object
domain.RawOddsByMarketID: domain.RawOddsByMarketID:
properties: properties:
@ -718,6 +727,35 @@ definitions:
static_updated_at: static_updated_at:
type: string type: string
type: object type: object
handlers.GetCashierRes:
properties:
created_at:
type: string
email:
type: string
email_verified:
type: boolean
first_name:
type: string
id:
type: integer
last_login:
type: string
last_name:
type: string
phone_number:
type: string
phone_verified:
type: boolean
role:
$ref: '#/definitions/domain.Role'
suspended:
type: boolean
suspended_at:
type: string
updated_at:
type: string
type: object
handlers.ManagersRes: handlers.ManagersRes:
properties: properties:
created_at: created_at:
@ -963,6 +1001,25 @@ definitions:
example: true example: true
type: boolean type: boolean
type: object type: object
handlers.UpdateUserSuspendReq:
properties:
suspended:
example: true
type: boolean
user_id:
example: 123
type: integer
required:
- suspended
- user_id
type: object
handlers.UpdateUserSuspendRes:
properties:
suspended:
type: boolean
user_id:
type: integer
type: object
handlers.UpdateWalletActiveReq: handlers.UpdateWalletActiveReq:
properties: properties:
is_active: is_active:
@ -1658,6 +1715,37 @@ paths:
summary: Gets bets by its branch id summary: Gets bets by its branch id
tags: tags:
- branch - branch
/branch/{id}/cashier:
get:
consumes:
- application/json
description: Gets branch cashiers
parameters:
- description: Branch ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.GetCashierRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets branch cashiers
tags:
- branch
/branch/{id}/operation: /branch/{id}/operation:
get: get:
consumes: consumes:
@ -2940,6 +3028,35 @@ paths:
summary: Check if phone number or email exist summary: Check if phone number or email exist
tags: tags:
- user - user
/user/delete/{id}:
delete:
consumes:
- application/json
description: Delete a user by their ID
parameters:
- description: User ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Delete user by ID
tags:
- user
/user/profile: /user/profile:
get: get:
consumes: consumes:
@ -3132,7 +3249,7 @@ paths:
"200": "200":
description: OK description: OK
schema: schema:
$ref: '#/definitions/response.APIResponse' $ref: '#/definitions/handlers.UserProfileRes'
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
@ -3148,6 +3265,36 @@ paths:
summary: Get user by id summary: Get user by id
tags: tags:
- user - user
/user/suspend:
post:
consumes:
- application/json
description: Suspend or unsuspend a user
parameters:
- description: Suspend or unsuspend a user
in: body
name: updateUserSuspend
required: true
schema:
$ref: '#/definitions/handlers.UpdateUserSuspendReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.UpdateUserSuspendRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Suspend or unsuspend a user
tags:
- user
/user/wallet: /user/wallet:
get: get:
consumes: consumes:

View File

@ -50,19 +50,19 @@ func (q *Queries) DeleteCompany(ctx context.Context, id int64) error {
} }
const GetAllCompanies = `-- name: GetAllCompanies :many const GetAllCompanies = `-- name: GetAllCompanies :many
SELECT id, name, admin_id, wallet_id, balance, is_active SELECT id, name, admin_id, wallet_id, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_with_wallets FROM companies_details
` `
func (q *Queries) GetAllCompanies(ctx context.Context) ([]CompaniesWithWallet, error) { func (q *Queries) GetAllCompanies(ctx context.Context) ([]CompaniesDetail, error) {
rows, err := q.db.Query(ctx, GetAllCompanies) rows, err := q.db.Query(ctx, GetAllCompanies)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []CompaniesWithWallet var items []CompaniesDetail
for rows.Next() { for rows.Next() {
var i CompaniesWithWallet var i CompaniesDetail
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
@ -70,6 +70,9 @@ func (q *Queries) GetAllCompanies(ctx context.Context) ([]CompaniesWithWallet, e
&i.WalletID, &i.WalletID,
&i.Balance, &i.Balance,
&i.IsActive, &i.IsActive,
&i.AdminFirstName,
&i.AdminLastName,
&i.AdminPhoneNumber,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -82,14 +85,14 @@ func (q *Queries) GetAllCompanies(ctx context.Context) ([]CompaniesWithWallet, e
} }
const GetCompanyByID = `-- name: GetCompanyByID :one const GetCompanyByID = `-- name: GetCompanyByID :one
SELECT id, name, admin_id, wallet_id, balance, is_active SELECT id, name, admin_id, wallet_id, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_with_wallets FROM companies_details
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesWithWallet, error) { func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail, error) {
row := q.db.QueryRow(ctx, GetCompanyByID, id) row := q.db.QueryRow(ctx, GetCompanyByID, id)
var i CompaniesWithWallet var i CompaniesDetail
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
@ -97,25 +100,28 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesWithWa
&i.WalletID, &i.WalletID,
&i.Balance, &i.Balance,
&i.IsActive, &i.IsActive,
&i.AdminFirstName,
&i.AdminLastName,
&i.AdminPhoneNumber,
) )
return i, err return i, err
} }
const SearchCompanyByName = `-- name: SearchCompanyByName :many const SearchCompanyByName = `-- name: SearchCompanyByName :many
SELECT id, name, admin_id, wallet_id, balance, is_active SELECT id, name, admin_id, wallet_id, balance, is_active, admin_first_name, admin_last_name, admin_phone_number
FROM companies_with_wallets FROM companies_details
WHERE name ILIKE '%' || $1 || '%' WHERE name ILIKE '%' || $1 || '%'
` `
func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text) ([]CompaniesWithWallet, error) { func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text) ([]CompaniesDetail, error) {
rows, err := q.db.Query(ctx, SearchCompanyByName, dollar_1) rows, err := q.db.Query(ctx, SearchCompanyByName, dollar_1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []CompaniesWithWallet var items []CompaniesDetail
for rows.Next() { for rows.Next() {
var i CompaniesWithWallet var i CompaniesDetail
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
@ -123,6 +129,9 @@ func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text)
&i.WalletID, &i.WalletID,
&i.Balance, &i.Balance,
&i.IsActive, &i.IsActive,
&i.AdminFirstName,
&i.AdminLastName,
&i.AdminPhoneNumber,
); err != nil { ); err != nil {
return nil, err return nil, err
} }

View File

@ -146,13 +146,16 @@ type BranchOperation struct {
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
type CompaniesWithWallet struct { type CompaniesDetail struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
AdminID int64 `json:"admin_id"` AdminID int64 `json:"admin_id"`
WalletID int64 `json:"wallet_id"` WalletID int64 `json:"wallet_id"`
Balance int64 `json:"balance"` Balance int64 `json:"balance"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
AdminFirstName string `json:"admin_first_name"`
AdminLastName string `json:"admin_last_name"`
AdminPhoneNumber pgtype.Text `json:"admin_phone_number"`
} }
type Company struct { type Company struct {

View File

@ -97,7 +97,8 @@ type CreateBetReq struct {
} }
type RandomBetReq struct { type RandomBetReq struct {
BranchID int64 `json:"branch_id" validate:"required" example:"1"` BranchID int64 `json:"branch_id" validate:"required" example:"1"`
NumberOfBets int64 `json:"number_of_bets" validate:"required" example:"1"`
} }
type CreateBetRes struct { type CreateBetRes struct {

View File

@ -11,12 +11,15 @@ type Company struct {
} }
type GetCompany struct { type GetCompany struct {
ID int64 ID int64
Name string Name string
AdminID int64 AdminID int64
WalletID int64 AdminFirstName string
WalletBalance Currency AdminLastName string
IsWalletActive bool AdminPhoneNumber string
WalletID int64
WalletBalance Currency
IsWalletActive bool
} }
type CreateCompany struct { type CreateCompany struct {

View File

@ -26,11 +26,14 @@ var SupportedLeagues = []int64{
// Basketball // Basketball
173998768, //NBA 173998768, //NBA
10041830, //NBA 10041830, //NBA
10049984, //WNBA
10037165, //German Bundesliga
10036608, //Italian Lega 1
10040795, //EuroLeague
// Ice Hockey // Ice Hockey
10037477, //NHL 10037477, //NHL
10037447, //AHL 10037447, //AHL
10069385, //IIHF World Championship 10069385, //IIHF World Championship
10040795, //EuroLeague
} }

View File

@ -64,3 +64,21 @@ func (o *OutcomeStatus) String() string {
return "UNKNOWN" return "UNKNOWN"
} }
} }
type TimeStatus int32
const (
TIME_STATUS_NOT_STARTED TimeStatus = 0
TIME_STATUS_IN_PLAY TimeStatus = 1
TIME_STATUS_TO_BE_FIXED TimeStatus = 2
TIME_STATUS_ENDED TimeStatus = 3
TIME_STATUS_POSTPONED TimeStatus = 4
TIME_STATUS_CANCELLED TimeStatus = 5
TIME_STATUS_WALKOVER TimeStatus = 6
TIME_STATUS_INTERRUPTED TimeStatus = 7
TIME_STATUS_ABANDONED TimeStatus = 8
TIME_STATUS_RETIRED TimeStatus = 9
TIME_STATUS_SUSPENDED TimeStatus = 10
TIME_STATUS_DECIDED_BY_FA TimeStatus = 11
TIME_STATUS_REMOVED TimeStatus = 99
)

View File

@ -43,6 +43,7 @@ type FootballResultResponse struct {
Stats struct { Stats struct {
Attacks []string `json:"attacks"` Attacks []string `json:"attacks"`
Corners []string `json:"corners"` Corners []string `json:"corners"`
HalfTimeCorners []string `json:"corner_h"`
DangerousAttacks []string `json:"dangerous_attacks"` DangerousAttacks []string `json:"dangerous_attacks"`
Goals []string `json:"goals"` Goals []string `json:"goals"`
OffTarget []string `json:"off_target"` OffTarget []string `json:"off_target"`
@ -94,7 +95,7 @@ type BasketballResultResponse struct {
Possession []string `json:"possession"` Possession []string `json:"possession"`
SuccessAttempts []string `json:"success_attempts"` SuccessAttempts []string `json:"success_attempts"`
TimeSpendInLead []string `json:"timespent_inlead"` TimeSpendInLead []string `json:"timespent_inlead"`
Timeuts []string `json:"time_outs"` TimeOuts []string `json:"time_outs"`
} `json:"stats"` } `json:"stats"`
Extra struct { Extra struct {
HomePos string `json:"home_pos"` HomePos string `json:"home_pos"`
@ -104,7 +105,7 @@ type BasketballResultResponse struct {
NumberOfPeriods string `json:"numberofperiods"` NumberOfPeriods string `json:"numberofperiods"`
PeriodLength string `json:"periodlength"` PeriodLength string `json:"periodlength"`
StadiumData map[string]string `json:"stadium_data"` StadiumData map[string]string `json:"stadium_data"`
Length string `json:"length"` Length int `json:"length"`
Round string `json:"round"` Round string `json:"round"`
} `json:"extra"` } `json:"extra"`
Events []map[string]string `json:"events"` Events []map[string]string `json:"events"`
@ -142,7 +143,7 @@ type IceHockeyResultResponse struct {
NumberOfPeriods string `json:"numberofperiods"` NumberOfPeriods string `json:"numberofperiods"`
PeriodLength string `json:"periodlength"` PeriodLength string `json:"periodlength"`
StadiumData map[string]string `json:"stadium_data"` StadiumData map[string]string `json:"stadium_data"`
Length string `json:"length"` Length int `json:"length"`
Round string `json:"round"` Round string `json:"round"`
} `json:"extra"` } `json:"extra"`
Events []map[string]string `json:"events"` Events []map[string]string `json:"events"`

View File

@ -3,12 +3,12 @@ package domain
type FootballMarket int64 type FootballMarket int64
const ( const (
FOOTBALL_FULL_TIME_RESULT FootballMarket = 40 //"full_time_result" FOOTBALL_FULL_TIME_RESULT FootballMarket = 40 //"full_time_result"
FOOTBALL_DOUBLE_CHANCE FootballMarket = 10114 //"double_chance" FOOTBALL_DOUBLE_CHANCE FootballMarket = 10114 //"double_chance"
FOOTBALL_GOALS_OVER_UNDER FootballMarket = 981 //"goals_over_under" FOOTBALL_GOALS_OVER_UNDER FootballMarket = 981 //"goals_over_under"
FOOTBALL_CORRECT_SCORE FootballMarket = 43 //"correct_score" FOOTBALL_CORRECT_SCORE FootballMarket = 43 //"correct_score"
FOOTBALL_ASIAN_HANDICAP FootballMarket = 938 //"asian_handicap" FOOTBALL_ASIAN_HANDICAP FootballMarket = 938 //"asian_handicap"
FOOTBALL_GOAL_LINE FootballMarket = 10143 //"goal_line" FOOTBALL_GOAL_LINE FootballMarket = 10143 //"goal_line"
FOOTBALL_HALF_TIME_RESULT FootballMarket = 1579 //"half_time_result" FOOTBALL_HALF_TIME_RESULT FootballMarket = 1579 //"half_time_result"
FOOTBALL_FIRST_HALF_ASIAN_HANDICAP FootballMarket = 50137 //"1st_half_asian_handicap" FOOTBALL_FIRST_HALF_ASIAN_HANDICAP FootballMarket = 50137 //"1st_half_asian_handicap"
@ -17,6 +17,13 @@ const (
FOOTBALL_GOALS_ODD_EVEN FootballMarket = 10111 //"goals_odd_even" FOOTBALL_GOALS_ODD_EVEN FootballMarket = 10111 //"goals_odd_even"
FOOTBALL_DRAW_NO_BET FootballMarket = 10544 //"draw_no_bet" FOOTBALL_DRAW_NO_BET FootballMarket = 10544 //"draw_no_bet"
FOOTBALL_CORNERS FootballMarket = 760 //"corners"
FOOTBALL_CORNERS_TWO_WAY FootballMarket = 10235 //"corners_2_way"
FOOTBALL_FIRST_HALF_CORNERS FootballMarket = 10539 //"first_half_corners"
FOOTBALL_ASIAN_TOTAL_CORNERS FootballMarket = 10164 //"asian_total_corners"
FOOTBALL_FIRST_HALF_ASIAN_CORNERS FootballMarket = 10233 //"1st_half_asian_corners"
FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN FootballMarket = 10206 //"1st_half_goals_odd_even"
FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN FootballMarket = 50433 //"2nd_half_goals_odd_even"
) )
@ -99,18 +106,25 @@ const (
var SupportedMarkets = map[int64]bool{ var SupportedMarkets = map[int64]bool{
// Football Markets // Football Markets
int64(FOOTBALL_FULL_TIME_RESULT): true, //"full_time_result" int64(FOOTBALL_FULL_TIME_RESULT): true, //"full_time_result"
int64(FOOTBALL_DOUBLE_CHANCE): true, //"double_chance" int64(FOOTBALL_DOUBLE_CHANCE): true, //"double_chance"
int64(FOOTBALL_GOALS_OVER_UNDER): true, //"goals_over_under" int64(FOOTBALL_GOALS_OVER_UNDER): true, //"goals_over_under"
int64(FOOTBALL_CORRECT_SCORE): true, //"correct_score" int64(FOOTBALL_CORRECT_SCORE): true, //"correct_score"
int64(FOOTBALL_ASIAN_HANDICAP): true, //"asian_handicap" int64(FOOTBALL_ASIAN_HANDICAP): true, //"asian_handicap"
int64(FOOTBALL_GOAL_LINE): true, //"goal_line" int64(FOOTBALL_GOAL_LINE): true, //"goal_line"
int64(FOOTBALL_HALF_TIME_RESULT): true, //"half_time_result" int64(FOOTBALL_HALF_TIME_RESULT): true, //"half_time_result"
int64(FOOTBALL_FIRST_HALF_ASIAN_HANDICAP): true, //"1st_half_asian_handicap" int64(FOOTBALL_FIRST_HALF_ASIAN_HANDICAP): true, //"1st_half_asian_handicap"
int64(FOOTBALL_FIRST_HALF_GOAL_LINE): true, //"1st_half_goal_line" int64(FOOTBALL_FIRST_HALF_GOAL_LINE): true, //"1st_half_goal_line"
int64(FOOTBALL_FIRST_TEAM_TO_SCORE): true, //"first_team_to_score" int64(FOOTBALL_FIRST_TEAM_TO_SCORE): true, //"first_team_to_score"
int64(FOOTBALL_GOALS_ODD_EVEN): true, //"goals_odd_even" int64(FOOTBALL_GOALS_ODD_EVEN): true, //"goals_odd_even"
int64(FOOTBALL_DRAW_NO_BET): true, //"draw_no_bet" int64(FOOTBALL_DRAW_NO_BET): true, //"draw_no_bet"
int64(FOOTBALL_CORNERS): true,
int64(FOOTBALL_CORNERS_TWO_WAY): true,
int64(FOOTBALL_FIRST_HALF_CORNERS): true,
int64(FOOTBALL_ASIAN_TOTAL_CORNERS): true,
int64(FOOTBALL_FIRST_HALF_ASIAN_CORNERS): true,
int64(FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN): true,
int64(FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN): true,
// Basketball Markets // Basketball Markets
int64(BASKETBALL_GAME_LINES): true, int64(BASKETBALL_GAME_LINES): true,

View File

@ -25,14 +25,17 @@ func convertDBCompany(dbCompany dbgen.Company) domain.Company {
} }
} }
func convertDBCompanyWithWallet(dbCompany dbgen.CompaniesWithWallet) domain.GetCompany { func convertDBCompanyDetails(dbCompany dbgen.CompaniesDetail) domain.GetCompany {
return domain.GetCompany{ return domain.GetCompany{
ID: dbCompany.ID, ID: dbCompany.ID,
Name: dbCompany.Name, Name: dbCompany.Name,
AdminID: dbCompany.AdminID, AdminID: dbCompany.AdminID,
WalletID: dbCompany.WalletID, WalletID: dbCompany.WalletID,
WalletBalance: domain.Currency(dbCompany.Balance), WalletBalance: domain.Currency(dbCompany.Balance),
IsWalletActive: dbCompany.IsActive, IsWalletActive: dbCompany.IsActive,
AdminFirstName: dbCompany.AdminFirstName,
AdminLastName: dbCompany.AdminLastName,
AdminPhoneNumber: dbCompany.AdminPhoneNumber.String,
} }
} }
@ -74,7 +77,7 @@ func (s *Store) GetAllCompanies(ctx context.Context) ([]domain.GetCompany, error
var companies []domain.GetCompany = make([]domain.GetCompany, 0, len(dbCompanies)) var companies []domain.GetCompany = make([]domain.GetCompany, 0, len(dbCompanies))
for _, dbCompany := range dbCompanies { for _, dbCompany := range dbCompanies {
companies = append(companies, convertDBCompanyWithWallet(dbCompany)) companies = append(companies, convertDBCompanyDetails(dbCompany))
} }
return companies, nil return companies, nil
@ -92,7 +95,7 @@ func (s *Store) SearchCompanyByName(ctx context.Context, name string) ([]domain.
var companies []domain.GetCompany = make([]domain.GetCompany, 0, len(dbCompanies)) var companies []domain.GetCompany = make([]domain.GetCompany, 0, len(dbCompanies))
for _, dbCompany := range dbCompanies { for _, dbCompany := range dbCompanies {
companies = append(companies, convertDBCompanyWithWallet(dbCompany)) companies = append(companies, convertDBCompanyDetails(dbCompany))
} }
return companies, nil return companies, nil
} }
@ -103,7 +106,7 @@ func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany
if err != nil { if err != nil {
return domain.GetCompany{}, err return domain.GetCompany{}, err
} }
return convertDBCompanyWithWallet(dbCompany), nil return convertDBCompanyDetails(dbCompany), nil
} }
func (s *Store) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) { func (s *Store) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (domain.Company, error) {

View File

@ -128,7 +128,7 @@ func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.Val
String: sportID.Value, String: sportID.Value,
Valid: sportID.Valid, Valid: sportID.Valid,
}, },
Limit: pgtype.Int4{ Limit: pgtype.Int4{
Int32: int32(limit.Value), Int32: int32(limit.Value),
Valid: limit.Valid, Valid: limit.Valid,
}, },

View File

@ -230,6 +230,22 @@ func (s *Store) UpdateUserCompany(ctx context.Context, id int64, companyID int64
} }
return nil return nil
} }
func (s *Store) UpdateUserSuspend(ctx context.Context, id int64, status bool) error {
err := s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{
ID: id,
Suspended: status,
SuspendedAt: pgtype.Timestamptz{
Time: time.Now(),
Valid: true,
},
})
if err != nil {
return err
}
return nil
}
func (s *Store) DeleteUser(ctx context.Context, id int64) error { func (s *Store) DeleteUser(ctx context.Context, id int64) error {
err := s.queries.DeleteUser(ctx, id) err := s.queries.DeleteUser(ctx, id)
if err != nil { if err != nil {

View File

@ -362,7 +362,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le
// TODO: Add the option of passing number of created events // TODO: Add the option of passing number of created events
var selectedUpcomingEvents []domain.UpcomingEvent var selectedUpcomingEvents []domain.UpcomingEvent
numEventsPerBet := random.Intn(4) + 1 //Eliminate the option of 0 numEventsPerBet := min(random.Intn(4)+1, len(events)) //Eliminate the option of 0
for i := 0; i < int(numEventsPerBet); i++ { for i := 0; i < int(numEventsPerBet); i++ {
randomIndex := random.Intn(len(events)) randomIndex := random.Intn(len(events))
@ -371,7 +371,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le
} }
s.logger.Info("Generating random bet events", "selectedUpcomingEvents", len(selectedUpcomingEvents)) // s.logger.Info("Generating random bet events", "selectedUpcomingEvents", len(selectedUpcomingEvents))
// Get market and odds for that // Get market and odds for that
var randomOdds []domain.CreateBetOutcome var randomOdds []domain.CreateBetOutcome
@ -395,7 +395,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le
return domain.CreateBetRes{}, ErrGenerateRandomOutcome return domain.CreateBetRes{}, ErrGenerateRandomOutcome
} }
s.logger.Info("Generated Random bet Outcome", "randomOdds", len(randomOdds)) // s.logger.Info("Generated Random bet Outcome", "randomOdds", len(randomOdds))
var cashoutID string var cashoutID string
@ -491,9 +491,9 @@ func (s *Service) CheckBetOutcomeForBet(ctx context.Context, betID int64) (domai
status = betOutcome.Status status = betOutcome.Status
case domain.OUTCOME_STATUS_WIN: case domain.OUTCOME_STATUS_WIN:
if betOutcome.Status == domain.OUTCOME_STATUS_LOSS { if betOutcome.Status == domain.OUTCOME_STATUS_LOSS {
status = domain.OUTCOME_STATUS_HALF status = domain.OUTCOME_STATUS_LOSS
} else if betOutcome.Status == domain.OUTCOME_STATUS_HALF { } else if betOutcome.Status == domain.OUTCOME_STATUS_HALF {
status = domain.OUTCOME_STATUS_VOID status = domain.OUTCOME_STATUS_HALF
} else if betOutcome.Status == domain.OUTCOME_STATUS_WIN { } else if betOutcome.Status == domain.OUTCOME_STATUS_WIN {
status = domain.OUTCOME_STATUS_WIN status = domain.OUTCOME_STATUS_WIN
} else if betOutcome.Status == domain.OUTCOME_STATUS_VOID { } else if betOutcome.Status == domain.OUTCOME_STATUS_VOID {
@ -509,16 +509,18 @@ func (s *Service) CheckBetOutcomeForBet(ctx context.Context, betID int64) (domai
} else if betOutcome.Status == domain.OUTCOME_STATUS_WIN { } else if betOutcome.Status == domain.OUTCOME_STATUS_WIN {
status = domain.OUTCOME_STATUS_LOSS status = domain.OUTCOME_STATUS_LOSS
} else if betOutcome.Status == domain.OUTCOME_STATUS_VOID { } else if betOutcome.Status == domain.OUTCOME_STATUS_VOID {
status = domain.OUTCOME_STATUS_VOID status = domain.OUTCOME_STATUS_LOSS
} else { } else {
status = domain.OUTCOME_STATUS_ERROR status = domain.OUTCOME_STATUS_ERROR
} }
case domain.OUTCOME_STATUS_VOID: case domain.OUTCOME_STATUS_VOID:
if betOutcome.Status == domain.OUTCOME_STATUS_VOID || if betOutcome.Status == domain.OUTCOME_STATUS_VOID ||
betOutcome.Status == domain.OUTCOME_STATUS_WIN || betOutcome.Status == domain.OUTCOME_STATUS_WIN ||
betOutcome.Status == domain.OUTCOME_STATUS_LOSS ||
betOutcome.Status == domain.OUTCOME_STATUS_HALF { betOutcome.Status == domain.OUTCOME_STATUS_HALF {
status = domain.OUTCOME_STATUS_VOID status = domain.OUTCOME_STATUS_VOID
} else if betOutcome.Status == domain.OUTCOME_STATUS_LOSS {
status = domain.OUTCOME_STATUS_LOSS
} else { } else {
status = domain.OUTCOME_STATUS_ERROR status = domain.OUTCOME_STATUS_ERROR
} }

View File

@ -100,7 +100,7 @@ func (s *service) FetchLiveEvents(ctx context.Context) error {
func (s *service) FetchUpcomingEvents(ctx context.Context) error { func (s *service) FetchUpcomingEvents(ctx context.Context) error {
// sportIDs := []int{1, 18, 17} // sportIDs := []int{1, 18, 17}
sportIDs := []int{18} sportIDs := []int{18, 17}
for _, sportID := range sportIDs { for _, sportID := range sportIDs {
var totalPages int = 1 var totalPages int = 1
@ -142,6 +142,7 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
} `json:"away"` } `json:"away"`
} `json:"results"` } `json:"results"`
} }
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
@ -164,7 +165,7 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error {
} }
if !slices.Contains(domain.SupportedLeagues, leagueID) { if !slices.Contains(domain.SupportedLeagues, leagueID) {
fmt.Printf("⚠️ Skipping league %s (%d) as it is not supported\n", ev.League.Name, leagueID)
skippedLeague = append(skippedLeague, ev.League.Name) skippedLeague = append(skippedLeague, ev.League.Name)
continue continue
} }
@ -172,7 +173,7 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error {
event := domain.UpcomingEvent{ event := domain.UpcomingEvent{
ID: ev.ID, ID: ev.ID,
SportID: ev.SportID, SportID: ev.SportID,
MatchName: ev.Home.Name, MatchName: "",
HomeTeam: ev.Home.Name, HomeTeam: ev.Home.Name,
AwayTeam: "", // handle nil safely AwayTeam: "", // handle nil safely
HomeTeamID: ev.Home.ID, HomeTeamID: ev.Home.ID,
@ -188,6 +189,7 @@ func (s *service) FetchUpcomingEvents(ctx context.Context) error {
if ev.Away != nil { if ev.Away != nil {
event.AwayTeam = ev.Away.Name event.AwayTeam = ev.Away.Name
event.AwayTeamID = ev.Away.ID event.AwayTeamID = ev.Away.ID
event.MatchName = ev.Home.Name + " vs " + ev.Away.Name
} }
err = s.store.SaveUpcomingEvent(ctx, event) err = s.store.SaveUpcomingEvent(ctx, event)
@ -234,7 +236,7 @@ func (s *service) GetExpiredUpcomingEvents(ctx context.Context) ([]domain.Upcomi
return s.store.GetExpiredUpcomingEvents(ctx) return s.store.GetExpiredUpcomingEvents(ctx)
} }
func (s *service) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.ValidInt64, offset domain.ValidInt64, leagueID domain.ValidString, sportID domain.ValidString, firstStartTime domain.ValidTime, lastStartTime domain.ValidTime) ([]domain.UpcomingEvent, int64, error){ func (s *service) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.ValidInt64, offset domain.ValidInt64, leagueID domain.ValidString, sportID domain.ValidString, firstStartTime domain.ValidTime, lastStartTime domain.ValidTime) ([]domain.UpcomingEvent, int64, error) {
return s.store.GetPaginatedUpcomingEvents(ctx, limit, offset, leagueID, sportID, firstStartTime, lastStartTime) return s.store.GetPaginatedUpcomingEvents(ctx, limit, offset, leagueID, sportID, firstStartTime, lastStartTime)
} }

View File

@ -246,7 +246,7 @@ func (s *ServiceImpl) storeSection(ctx context.Context, eventID, fi, sectionName
var marketIDint int var marketIDint int
err := json.Unmarshal(market.ID, &marketIDint) err := json.Unmarshal(market.ID, &marketIDint)
if err != nil { if err != nil {
s.logger.Error("Invalid market id") s.logger.Error("Invalid market id", "marketID", marketIDstr, "marketName", market.Name)
errs = append(errs, err) errs = append(errs, err)
} }
} }

View File

@ -85,6 +85,8 @@ func checkMultiOutcome(outcome domain.OutcomeStatus, secondOutcome domain.Outcom
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("❌ mutli outcome: %v -> %v \n", outcome.String(), secondOutcome.String()) return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("❌ mutli outcome: %v -> %v \n", outcome.String(), secondOutcome.String())
} }
// fmt.Printf("| Multi Outcome | %v -> %v \n", outcome.String(), secondOutcome.String())
switch outcome { switch outcome {
case domain.OUTCOME_STATUS_PENDING: case domain.OUTCOME_STATUS_PENDING:
return secondOutcome, nil return secondOutcome, nil
@ -94,7 +96,7 @@ func checkMultiOutcome(outcome domain.OutcomeStatus, secondOutcome domain.Outcom
} else if secondOutcome == domain.OUTCOME_STATUS_LOSS { } else if secondOutcome == domain.OUTCOME_STATUS_LOSS {
return domain.OUTCOME_STATUS_LOSS, nil return domain.OUTCOME_STATUS_LOSS, nil
} else if secondOutcome == domain.OUTCOME_STATUS_HALF { } else if secondOutcome == domain.OUTCOME_STATUS_HALF {
return domain.OUTCOME_STATUS_HALF, nil return domain.OUTCOME_STATUS_VOID, nil
} else if secondOutcome == domain.OUTCOME_STATUS_VOID { } else if secondOutcome == domain.OUTCOME_STATUS_VOID {
return domain.OUTCOME_STATUS_HALF, nil return domain.OUTCOME_STATUS_HALF, nil
} else { } else {
@ -107,14 +109,14 @@ func checkMultiOutcome(outcome domain.OutcomeStatus, secondOutcome domain.Outcom
secondOutcome == domain.OUTCOME_STATUS_HALF { secondOutcome == domain.OUTCOME_STATUS_HALF {
return domain.OUTCOME_STATUS_LOSS, nil return domain.OUTCOME_STATUS_LOSS, nil
} else if secondOutcome == domain.OUTCOME_STATUS_VOID { } else if secondOutcome == domain.OUTCOME_STATUS_VOID {
return domain.OUTCOME_STATUS_HALF, nil return domain.OUTCOME_STATUS_VOID, nil
} else { } else {
fmt.Printf("❌ multi outcome: %v -> %v \n", outcome.String(), secondOutcome.String()) fmt.Printf("❌ multi outcome: %v -> %v \n", outcome.String(), secondOutcome.String())
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid multi outcome") return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid multi outcome")
} }
case domain.OUTCOME_STATUS_VOID: case domain.OUTCOME_STATUS_VOID:
if secondOutcome == domain.OUTCOME_STATUS_WIN || secondOutcome == domain.OUTCOME_STATUS_LOSS { if secondOutcome == domain.OUTCOME_STATUS_WIN || secondOutcome == domain.OUTCOME_STATUS_LOSS {
return domain.OUTCOME_STATUS_HALF, nil return domain.OUTCOME_STATUS_VOID, nil
} else if secondOutcome == domain.OUTCOME_STATUS_VOID || secondOutcome == domain.OUTCOME_STATUS_HALF { } else if secondOutcome == domain.OUTCOME_STATUS_VOID || secondOutcome == domain.OUTCOME_STATUS_HALF {
return domain.OUTCOME_STATUS_VOID, nil return domain.OUTCOME_STATUS_VOID, nil
} else { } else {
@ -123,7 +125,7 @@ func checkMultiOutcome(outcome domain.OutcomeStatus, secondOutcome domain.Outcom
} }
case domain.OUTCOME_STATUS_HALF: case domain.OUTCOME_STATUS_HALF:
if secondOutcome == domain.OUTCOME_STATUS_WIN || secondOutcome == domain.OUTCOME_STATUS_HALF { if secondOutcome == domain.OUTCOME_STATUS_WIN || secondOutcome == domain.OUTCOME_STATUS_HALF {
return domain.OUTCOME_STATUS_HALF, nil return domain.OUTCOME_STATUS_VOID, nil
} else if secondOutcome == domain.OUTCOME_STATUS_LOSS { } else if secondOutcome == domain.OUTCOME_STATUS_LOSS {
return domain.OUTCOME_STATUS_LOSS, nil return domain.OUTCOME_STATUS_LOSS, nil
} else if secondOutcome == domain.OUTCOME_STATUS_VOID { } else if secondOutcome == domain.OUTCOME_STATUS_VOID {
@ -139,6 +141,8 @@ func checkMultiOutcome(outcome domain.OutcomeStatus, secondOutcome domain.Outcom
} }
// Asian Handicap betting is a type of betting that eliminates the possibility of a draw by giving one team a virtual advantage or disadvantage. // Asian Handicap betting is a type of betting that eliminates the possibility of a draw by giving one team a virtual advantage or disadvantage.
// When the handicap has two values like "+0.5, +1.0" or "-0.5, -1.0", then it a multi outcome bet
// .
// //
// { // {
// "id": "548319135", // "id": "548319135",
@ -178,26 +182,32 @@ func evaluateAsianHandicap(outcome domain.BetOutcome, score struct{ Home, Away i
if err != nil { if err != nil {
return domain.OUTCOME_STATUS_ERROR, err return domain.OUTCOME_STATUS_ERROR, err
} }
continue
} }
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS) newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS)
if err != nil { if err != nil {
return domain.OUTCOME_STATUS_ERROR, err return domain.OUTCOME_STATUS_ERROR, err
} }
continue
} else if adjustedHomeScore < adjustedAwayScore { } else if adjustedHomeScore < adjustedAwayScore {
if outcome.OddHeader == "2" { if outcome.OddHeader == "2" {
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_WIN) newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_WIN)
if err != nil { if err != nil {
return domain.OUTCOME_STATUS_ERROR, err return domain.OUTCOME_STATUS_ERROR, err
} }
continue
} }
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS) newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS)
if err != nil { if err != nil {
return domain.OUTCOME_STATUS_ERROR, err return domain.OUTCOME_STATUS_ERROR, err
} }
} continue
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_VOID) } else if adjustedHomeScore == adjustedAwayScore {
if err != nil { newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_VOID)
return domain.OUTCOME_STATUS_ERROR, err if err != nil {
return domain.OUTCOME_STATUS_ERROR, err
}
continue
} }
} }
return newOutcome, nil return newOutcome, nil
@ -306,24 +316,60 @@ func evaluateGoalsOddEven(outcome domain.BetOutcome, score struct{ Home, Away in
return domain.OUTCOME_STATUS_LOSS, nil return domain.OUTCOME_STATUS_LOSS, nil
} }
func evaluateTeamOddEven(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
switch outcome.OddHeader {
case "1":
if outcome.OddHandicap == "Odd" {
if score.Home%2 == 1 {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
} else if outcome.OddHandicap == "Even" {
if score.Home%2 == 0 {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
} else {
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd handicap: %s", outcome.OddHandicap)
}
case "2":
if outcome.OddHandicap == "Odd" {
if score.Away%2 == 1 {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
} else if outcome.OddHandicap == "Even" {
if score.Away%2 == 0 {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
} else {
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd handicap: %s", outcome.OddHandicap)
}
default:
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
}
}
// Double Chance betting is a type of bet where the bettor predicts two of the three possible outcomes of a match. // Double Chance betting is a type of bet where the bettor predicts two of the three possible outcomes of a match.
func evaluateDoubleChance(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { func evaluateDoubleChance(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
isHomeWin := score.Home > score.Away isHomeWin := score.Home > score.Away
isDraw := score.Home == score.Away isDraw := score.Home == score.Away
isAwayWin := score.Away > score.Home isAwayWin := score.Away > score.Home
switch outcome.OddName { switch outcome.OddName {
case "1 or Draw", (outcome.HomeTeamName + " or " + "Draw"): case "1 or Draw", (outcome.HomeTeamName + " or " + "Draw"), ("Draw" + " or " + outcome.HomeTeamName):
if isHomeWin || isDraw { if isHomeWin || isDraw {
return domain.OUTCOME_STATUS_WIN, nil return domain.OUTCOME_STATUS_WIN, nil
} }
return domain.OUTCOME_STATUS_LOSS, nil return domain.OUTCOME_STATUS_LOSS, nil
case "Draw or 2", ("Draw" + " or " + outcome.AwayTeamName): case "Draw or 2", ("Draw" + " or " + outcome.AwayTeamName), (outcome.AwayTeamName + " or " + "Draw"):
if isDraw || isAwayWin { if isDraw || isAwayWin {
return domain.OUTCOME_STATUS_WIN, nil return domain.OUTCOME_STATUS_WIN, nil
} }
return domain.OUTCOME_STATUS_LOSS, nil return domain.OUTCOME_STATUS_LOSS, nil
case "1 or 2", (outcome.HomeTeamName + " or " + outcome.AwayTeamName): case "1 or 2", (outcome.HomeTeamName + " or " + outcome.AwayTeamName), (outcome.AwayTeamName + " or " + outcome.HomeTeamName):
if isHomeWin || isAwayWin { if isHomeWin || isAwayWin {
return domain.OUTCOME_STATUS_WIN, nil return domain.OUTCOME_STATUS_WIN, nil
} }
@ -346,6 +392,34 @@ func evaluateDrawNoBet(outcome domain.BetOutcome, score struct{ Home, Away int }
return domain.OUTCOME_STATUS_LOSS, nil return domain.OUTCOME_STATUS_LOSS, nil
} }
func evaluateCorners(outcome domain.BetOutcome, corners struct{ Home, Away int }) (domain.OutcomeStatus, error) {
totalCorners := corners.Home + corners.Away
threshold, err := strconv.ParseFloat(outcome.OddName, 10)
if err != nil {
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName)
}
switch outcome.OddHeader {
case "Over":
if totalCorners > int(threshold) {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
case "Under":
if totalCorners < int(threshold) {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
case "Exactly":
if totalCorners == int(threshold) {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
default:
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
}
}
// Basketball evaluations // Basketball evaluations
// Game Lines is an aggregate of money line, spread and total betting markets in one // Game Lines is an aggregate of money line, spread and total betting markets in one
@ -715,6 +789,30 @@ func evaluateHighestScoringQuarter(outcome domain.BetOutcome, firstScore struct{
return domain.OUTCOME_STATUS_LOSS, nil return domain.OUTCOME_STATUS_LOSS, nil
} }
// Team With Highest Scoring Quarter betting is a type of bet where the bettor predicts which team will have the highest score in a specific quarter.
func evaluateTeamWithHighestScoringQuarter(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }, thirdScore struct{ Home, Away int }, fourthScore struct{ Home, Away int }) (domain.OutcomeStatus, error) {
homeTeamHighestQuarter := max(firstScore.Home, secondScore.Home, thirdScore.Home, fourthScore.Home)
awayTeamHighestQuarter := max(firstScore.Away, secondScore.Away, thirdScore.Away, fourthScore.Away)
switch outcome.OddName {
case "1":
if homeTeamHighestQuarter > awayTeamHighestQuarter {
return domain.OUTCOME_STATUS_WIN, nil
}
case "2":
if awayTeamHighestQuarter > homeTeamHighestQuarter {
return domain.OUTCOME_STATUS_WIN, nil
}
case "Tie":
if homeTeamHighestQuarter == awayTeamHighestQuarter {
return domain.OUTCOME_STATUS_WIN, nil
}
default:
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
}
return domain.OUTCOME_STATUS_LOSS, nil
}
// Handicap and Total betting is a combination of spread betting and total points betting // Handicap and Total betting is a combination of spread betting and total points betting
// where the bettor predicts the outcome of a match with a point spread and the total number of points scored is over or under a specified number. // where the bettor predicts the outcome of a match with a point spread and the total number of points scored is over or under a specified number.
func evaluateHandicapAndTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { func evaluateHandicapAndTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {

View File

@ -34,8 +34,9 @@ func NewService(repo *repository.Store, cfg *config.Config, logger *slog.Logger,
} }
} }
type ResultCheck struct { var (
} ErrEventIsNotActive = fmt.Errorf("Event has been cancelled or postponed")
)
func (s *Service) FetchAndProcessResults(ctx context.Context) error { func (s *Service) FetchAndProcessResults(ctx context.Context) error {
// TODO: Optimize this because there could be many bet outcomes for the same odd // TODO: Optimize this because there could be many bet outcomes for the same odd
@ -72,7 +73,7 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
if outcome.Expires.After(time.Now()) { if outcome.Expires.After(time.Now()) {
isDeleted = false isDeleted = false
s.logger.Info("Outcome is not expired yet", "event_id", event.ID, "outcome_id", outcome.ID) s.logger.Warn("Outcome is not expired yet", "event_id", event.ID, "outcome_id", outcome.ID)
continue continue
} }
@ -85,6 +86,10 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
// TODO: optimize this because the result is being fetched for each outcome which will have the same event id but different market id // TODO: optimize this because the result is being fetched for each outcome which will have the same event id but different market id
result, err := s.fetchResult(ctx, outcome.EventID, outcome.OddID, outcome.MarketID, sportID, outcome) result, err := s.fetchResult(ctx, outcome.EventID, outcome.OddID, outcome.MarketID, sportID, outcome)
if err != nil { if err != nil {
if err == ErrEventIsNotActive {
s.logger.Warn("Event is not active", "event_id", outcome.EventID, "error", err)
continue
}
fmt.Printf("❌ failed to parse 🎲 outcomes '%v' for event %v(%v) (%d/%d) \n", fmt.Printf("❌ failed to parse 🎲 outcomes '%v' for event %v(%v) (%d/%d) \n",
outcome.MarketName, outcome.MarketName,
event.HomeTeam+" "+event.AwayTeam, event.ID, event.HomeTeam+" "+event.AwayTeam, event.ID,
@ -123,13 +128,14 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
} }
continue continue
} }
fmt.Printf("🧾 Updating bet status for event %v (%d/%d) to %v\n", event.ID, j+1, len(outcomes), status.String()) fmt.Printf("🧾 Updating bet %v - event %v (%d/%d) to %v\n", outcome.BetID, event.ID, j+1, len(outcomes), status.String())
err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status) err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status)
if err != nil { if err != nil {
s.logger.Error("Failed to update bet status", "event id", outcome.EventID, "error", err) s.logger.Error("Failed to update bet status", "event id", outcome.EventID, "error", err)
continue continue
} }
fmt.Printf("✅ Successfully updated 🎫 Bet for event %v(%v) (%d/%d) \n", fmt.Printf("✅ Successfully updated 🎫 Bet %v - event %v(%v) (%d/%d) \n",
outcome.BetID,
event.HomeTeam+" "+event.AwayTeam, event.ID, event.HomeTeam+" "+event.AwayTeam, event.ID,
j+1, len(outcomes)) j+1, len(outcomes))
@ -273,6 +279,34 @@ func (s *Service) fetchResult(ctx context.Context, eventID, oddID, marketID, spo
} }
func (s *Service) parseTimeStatus(timeStatusStr string) (bool, error) {
timeStatusParsed, err := strconv.ParseInt(strings.TrimSpace(timeStatusStr), 10, 64)
if err != nil {
s.logger.Error("Failed to parse time status", "time_status", timeStatusStr, "error", err)
return false, fmt.Errorf("failed to parse time status: %w", err)
}
timeStatus := domain.TimeStatus(timeStatusParsed)
switch timeStatus {
case domain.TIME_STATUS_NOT_STARTED, domain.TIME_STATUS_IN_PLAY, domain.TIME_STATUS_TO_BE_FIXED, domain.TIME_STATUS_ENDED:
return true, nil
case domain.TIME_STATUS_POSTPONED,
domain.TIME_STATUS_CANCELLED,
domain.TIME_STATUS_WALKOVER,
domain.TIME_STATUS_INTERRUPTED,
domain.TIME_STATUS_ABANDONED,
domain.TIME_STATUS_RETIRED,
domain.TIME_STATUS_SUSPENDED,
domain.TIME_STATUS_DECIDED_BY_FA,
domain.TIME_STATUS_REMOVED:
return false, nil
default:
s.logger.Error("Invalid time status", "time_status", timeStatus)
return false, fmt.Errorf("invalid time status: %d", timeStatus)
}
}
func (s *Service) parseFootball(resultRes json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { func (s *Service) parseFootball(resultRes json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
var fbResp domain.FootballResultResponse var fbResp domain.FootballResultResponse
if err := json.Unmarshal(resultRes, &fbResp); err != nil { if err := json.Unmarshal(resultRes, &fbResp); err != nil {
@ -280,16 +314,24 @@ func (s *Service) parseFootball(resultRes json.RawMessage, eventID, oddID, marke
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
result := fbResp result := fbResp
if result.TimeStatus != "3" {
s.logger.Warn("Match not yet completed", "event_id", eventID) isEventActive, err := s.parseTimeStatus(result.TimeStatus)
return domain.CreateResult{}, fmt.Errorf("match not yet completed") if err != nil {
s.logger.Error("Failed to parse time status", "event_id", eventID, "error", err)
return domain.CreateResult{}, err
}
if !isEventActive {
s.logger.Warn("Event is not active", "event_id", eventID)
return domain.CreateResult{}, ErrEventIsNotActive
} }
finalScore := parseSS(result.SS) finalScore := parseSS(result.SS)
firstHalfScore := parseSS(fmt.Sprintf("%s-%s", result.Scores.FirstHalf.Home, result.Scores.FirstHalf.Away)) firstHalfScore := parseScore(result.Scores.FirstHalf.Home, result.Scores.FirstHalf.Away)
secondHalfScore := parseScore(result.Scores.SecondHalf.Home, result.Scores.SecondHalf.Away)
corners := parseStats(result.Stats.Corners) corners := parseStats(result.Stats.Corners)
status, err := s.evaluateFootballOutcome(outcome, finalScore, firstHalfScore, corners, result.Events) halfTimeCorners := parseStats(result.Stats.HalfTimeCorners)
status, err := s.evaluateFootballOutcome(outcome, finalScore, firstHalfScore, secondHalfScore, corners, halfTimeCorners, result.Events)
if err != nil { if err != nil {
s.logger.Error("Failed to evaluate football outcome", "event_id", eventID, "market_id", marketID, "error", err) s.logger.Error("Failed to evaluate football outcome", "event_id", eventID, "market_id", marketID, "error", err)
return domain.CreateResult{}, err return domain.CreateResult{}, err
@ -309,12 +351,17 @@ func (s *Service) parseFootball(resultRes json.RawMessage, eventID, oddID, marke
func (s *Service) parseBasketball(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { func (s *Service) parseBasketball(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
var basketBallRes domain.BasketballResultResponse var basketBallRes domain.BasketballResultResponse
if err := json.Unmarshal(response, &basketBallRes); err != nil { if err := json.Unmarshal(response, &basketBallRes); err != nil {
s.logger.Error("Failed to unmarshal football result", "event_id", eventID, "error", err) s.logger.Error("Failed to unmarshal basketball result", "event_id", eventID, "error", err)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
if basketBallRes.TimeStatus != "3" { isEventActive, err := s.parseTimeStatus(basketBallRes.TimeStatus)
s.logger.Warn("Match not yet completed", "event_id", eventID) if err != nil {
return domain.CreateResult{}, fmt.Errorf("match not yet completed") s.logger.Error("Failed to parse time status", "event_id", eventID, "error", err)
return domain.CreateResult{}, err
}
if !isEventActive {
s.logger.Warn("Event is not active", "event_id", eventID)
return domain.CreateResult{}, ErrEventIsNotActive
} }
status, err := s.evaluateBasketballOutcome(outcome, basketBallRes) status, err := s.evaluateBasketballOutcome(outcome, basketBallRes)
@ -337,12 +384,17 @@ func (s *Service) parseBasketball(response json.RawMessage, eventID, oddID, mark
func (s *Service) parseIceHockey(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { func (s *Service) parseIceHockey(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
var iceHockeyRes domain.IceHockeyResultResponse var iceHockeyRes domain.IceHockeyResultResponse
if err := json.Unmarshal(response, &iceHockeyRes); err != nil { if err := json.Unmarshal(response, &iceHockeyRes); err != nil {
s.logger.Error("Failed to unmarshal football result", "event_id", eventID, "error", err) s.logger.Error("Failed to unmarshal ice hockey result", "event_id", eventID, "error", err)
return domain.CreateResult{}, err return domain.CreateResult{}, err
} }
if iceHockeyRes.TimeStatus != "3" { isEventActive, err := s.parseTimeStatus(iceHockeyRes.TimeStatus)
s.logger.Warn("Match not yet completed", "event_id", eventID) if err != nil {
return domain.CreateResult{}, fmt.Errorf("match not yet completed") s.logger.Error("Failed to parse time status", "event_id", eventID, "error", err)
return domain.CreateResult{}, err
}
if !isEventActive {
s.logger.Warn("Event is not active", "event_id", eventID)
return domain.CreateResult{}, ErrEventIsNotActive
} }
status, err := s.evaluateIceHockeyOutcome(outcome, iceHockeyRes) status, err := s.evaluateIceHockeyOutcome(outcome, iceHockeyRes)
@ -388,7 +440,10 @@ func parseStats(stats []string) struct{ Home, Away int } {
} }
// evaluateOutcome determines the outcome status based on market type and odd // evaluateOutcome determines the outcome status based on market type and odd
func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore, firstHalfScore struct{ Home, Away int }, corners struct{ Home, Away int }, events []map[string]string) (domain.OutcomeStatus, error) { func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore,
firstHalfScore struct{ Home, Away int }, secondHalfScore struct{ Home, Away int },
corners struct{ Home, Away int }, halfTimeCorners struct{ Home, Away int },
events []map[string]string) (domain.OutcomeStatus, error) {
if !domain.SupportedMarkets[outcome.MarketID] { if !domain.SupportedMarkets[outcome.MarketID] {
s.logger.Warn("Unsupported market type", "market_name", outcome.MarketName) s.logger.Warn("Unsupported market type", "market_name", outcome.MarketName)
@ -420,6 +475,21 @@ func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore,
return evaluateDoubleChance(outcome, finalScore) return evaluateDoubleChance(outcome, finalScore)
case int64(domain.FOOTBALL_DRAW_NO_BET): case int64(domain.FOOTBALL_DRAW_NO_BET):
return evaluateDrawNoBet(outcome, finalScore) return evaluateDrawNoBet(outcome, finalScore)
case int64(domain.FOOTBALL_CORNERS):
return evaluateCorners(outcome, corners)
case int64(domain.FOOTBALL_CORNERS_TWO_WAY):
return evaluateCorners(outcome, corners)
case int64(domain.FOOTBALL_FIRST_HALF_CORNERS):
return evaluateCorners(outcome, halfTimeCorners)
case int64(domain.FOOTBALL_ASIAN_TOTAL_CORNERS):
return evaluateCorners(outcome, corners)
case int64(domain.FOOTBALL_FIRST_HALF_ASIAN_CORNERS):
return evaluateCorners(outcome, halfTimeCorners)
case int64(domain.FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN):
return evaluateGoalsOddEven(outcome, firstHalfScore)
case int64(domain.FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN):
return evaluateGoalsOddEven(outcome, secondHalfScore)
default: default:
s.logger.Warn("Market type not implemented", "market_name", outcome.MarketName) s.logger.Warn("Market type not implemented", "market_name", outcome.MarketName)
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("market type not implemented: %s", outcome.MarketName) return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("market type not implemented: %s", outcome.MarketName)
@ -456,7 +526,9 @@ func (s *Service) evaluateBasketballOutcome(outcome domain.BetOutcome, res domai
case int64(domain.BASKETBALL_GAME_TOTAL_ODD_EVEN): case int64(domain.BASKETBALL_GAME_TOTAL_ODD_EVEN):
return evaluateGoalsOddEven(outcome, finalScore) return evaluateGoalsOddEven(outcome, finalScore)
case int64(domain.BASKETBALL_TEAM_TOTALS): case int64(domain.BASKETBALL_TEAM_TOTALS):
return evaluateGoalsOddEven(outcome, finalScore) return evaluateTeamTotal(outcome, finalScore)
case int64(domain.BASKETBALL_TEAM_TOTAL_ODD_EVEN):
return evaluateTeamOddEven(outcome, finalScore)
case int64(domain.BASKETBALL_FIRST_HALF): case int64(domain.BASKETBALL_FIRST_HALF):
return evaluateGameLines(outcome, firstHalfScore) return evaluateGameLines(outcome, firstHalfScore)
@ -487,6 +559,11 @@ func (s *Service) evaluateBasketballOutcome(outcome domain.BetOutcome, res domai
return evaluateDoubleChance(outcome, firstQuarter) return evaluateDoubleChance(outcome, firstQuarter)
case int64(domain.BASKETBALL_HIGHEST_SCORING_QUARTER): case int64(domain.BASKETBALL_HIGHEST_SCORING_QUARTER):
return evaluateHighestScoringQuarter(outcome, firstQuarter, secondQuarter, thirdQuarter, fourthQuarter) return evaluateHighestScoringQuarter(outcome, firstQuarter, secondQuarter, thirdQuarter, fourthQuarter)
case int64(domain.BASKETBALL_FIRST_QUARTER_RESULT_AND_TOTAL):
return evaluateResultAndTotal(outcome, firstQuarter)
case int64(domain.BASKETBALL_TEAM_WITH_HIGHEST_SCORING_QUARTER):
return evaluateTeamWithHighestScoringQuarter(outcome, firstQuarter, secondQuarter, thirdQuarter, fourthQuarter)
default: default:
s.logger.Warn("Market type not implemented", "market_name", outcome.MarketName) s.logger.Warn("Market type not implemented", "market_name", outcome.MarketName)

View File

@ -15,6 +15,7 @@ type UserStore interface {
GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error) GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error)
UpdateUser(ctx context.Context, user domain.UpdateUserReq) error UpdateUser(ctx context.Context, user domain.UpdateUserReq) error
UpdateUserCompany(ctx context.Context, id int64, companyID int64) error UpdateUserCompany(ctx context.Context, id int64, companyID int64) error
UpdateUserSuspend(ctx context.Context, id int64, status bool) error
DeleteUser(ctx context.Context, id int64) error DeleteUser(ctx context.Context, id int64) error
CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error)
GetUserByEmail(ctx context.Context, email string) (domain.User, error) GetUserByEmail(ctx context.Context, email string) (domain.User, error)

View File

@ -20,7 +20,11 @@ func (s *Service) UpdateUser(ctx context.Context, user domain.UpdateUserReq) err
func (s *Service) UpdateUserCompany(ctx context.Context, id int64, companyID int64) error { func (s *Service) UpdateUserCompany(ctx context.Context, id int64, companyID int64) error {
// update user // update user
return s.userStore.UpdateUserCompany(ctx, id, companyID) return s.userStore.UpdateUserCompany(ctx, id, companyID)
}
func (s *Service) UpdateUserSuspend(ctx context.Context, id int64, status bool) error {
// update user
return s.userStore.UpdateUserSuspend(ctx, id, status)
} }
func (s *Service) GetUserByID(ctx context.Context, id int64) (domain.User, error) { func (s *Service) GetUserByID(ctx context.Context, id int64) (domain.User, error) {
return s.userStore.GetUserByID(ctx, id) return s.userStore.GetUserByID(ctx, id)

View File

@ -21,53 +21,24 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
spec string spec string
task func() task func()
}{ }{
// {
// spec: "0 0 * * * *", // Every 1 hour
// task: func() {
// if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
// log.Printf("FetchUpcomingEvents error: %v", err)
// }
// },
// },
// {
// spec: "*/5 * * * * *", // Every 5 seconds
// task: func() {
// if err := eventService.FetchLiveEvents(context.Background()); err != nil {
// log.Printf("FetchLiveEvents error: %v", err)
// }
// },
// },
// {
// spec: "0 */15 * * * *", // Every 15 minutes
// task: func() {
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
// log.Printf("FetchNonLiveOdds error: %v", err)
// }
// },
// },
// {
// spec: "0 */15 * * * *",
// task: func() {
// log.Println("Fetching results for upcoming events...")
// upcomingEvents, err := eventService.GetAllUpcomingEvents(context.Background())
// if err != nil {
// log.Printf("Failed to fetch upcoming events: %v", err)
// return
// }
// for _, event := range upcomingEvents {
// if err := resultService.FetchAndStoreResult(context.Background(), event.ID); err != nil {
// log.Printf(" Failed to fetch/store result for event %s: %v", event.ID, err)
// } else {
// log.Printf(" Successfully stored result for event %s", event.ID)
// }
// }
// },
// },
{ {
spec: "0 */15 * * * *", spec: "0 0 * * * *", // Every 1 hour
task: func() {
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
log.Printf("FetchUpcomingEvents error: %v", err)
}
},
},
{
spec: "0 */15 * * * *", // Every 15 minutes
task: func() {
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
log.Printf("FetchNonLiveOdds error: %v", err)
}
},
},
{
spec: "0 */15 * * * *", // Every 15 Minutes
task: func() { task: func() {
log.Println("Fetching results for upcoming events...") log.Println("Fetching results for upcoming events...")
@ -81,7 +52,6 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
} }
for _, job := range schedule { for _, job := range schedule {
job.task()
if _, err := c.AddFunc(job.spec, job.task); err != nil { if _, err := c.AddFunc(job.spec, job.task); err != nil {
log.Fatalf("Failed to schedule cron job: %v", err) log.Fatalf("Failed to schedule cron job: %v", err)
} }

View File

@ -116,17 +116,20 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
} }
res, err := h.betSvc.PlaceRandomBet(c.Context(), userID, req.BranchID, leagueID, sportID, firstStartTime, lastStartTime) var res domain.CreateBetRes
var err error
for i := 0; i < int(req.NumberOfBets); i++ {
res, err = h.betSvc.PlaceRandomBet(c.Context(), userID, req.BranchID, leagueID, sportID, firstStartTime, lastStartTime)
if err != nil { if err != nil {
h.logger.Error("Random Bet failed", "error", err) h.logger.Error("Random Bet failed", "error", err)
switch err { switch err {
case bet.ErrNoEventsAvailable: case bet.ErrNoEventsAvailable:
return fiber.NewError(fiber.StatusBadRequest, "No events found") return fiber.NewError(fiber.StatusBadRequest, "No events found")
}
return fiber.NewError(fiber.StatusInternalServerError, "Unable to create random bet")
} }
return fiber.NewError(fiber.StatusInternalServerError, "Unable to create random bet")
} }
return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil) return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil)
} }
@ -177,8 +180,9 @@ func (h *Handler) GetBetByID(c *fiber.Ctx) error {
bet, err := h.betSvc.GetBetByID(c.Context(), id) bet, err := h.betSvc.GetBetByID(c.Context(), id)
if err != nil { if err != nil {
// TODO: handle all the errors types
h.logger.Error("Failed to get bet by ID", "betID", id, "error", err) h.logger.Error("Failed to get bet by ID", "betID", id, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bet") return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
} }
res := domain.ConvertBet(bet) res := domain.ConvertBet(bet)

View File

@ -4,6 +4,7 @@ import (
"strconv" "strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -492,6 +493,64 @@ func (h *Handler) GetBranchOperations(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Branch Operations retrieved successfully", result, nil) return response.WriteJSON(c, fiber.StatusOK, "Branch Operations retrieved successfully", result, nil)
} }
// GetBranchCashiers godoc
// @Summary Gets branch cashiers
// @Description Gets branch cashiers
// @Tags branch
// @Accept json
// @Produce json
// @Param id path int true "Branch ID"
// @Success 200 {array} GetCashierRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /branch/{id}/cashier [get]
func (h *Handler) GetBranchCashiers(c *fiber.Ctx) error {
branchID := c.Params("id")
id, err := strconv.ParseInt(branchID, 10, 64)
if err != nil {
h.logger.Error("Invalid branch ID", "branchID", branchID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid branch ID", err, nil)
}
cashiers, err := h.userSvc.GetCashiersByBranch(c.Context(), id)
if err != nil {
h.logger.Error("Failed to get cashier by branch ID", "branchID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve cashier", err, nil)
}
var result []GetCashierRes = make([]GetCashierRes, 0, len(cashiers))
for _, cashier := range cashiers {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), cashier.ID)
if err != nil {
if err == authentication.ErrRefreshTokenNotFound {
lastLogin = &cashier.CreatedAt
} else {
h.logger.Error("Failed to get user last login", "userID", cashier.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
}
result = append(result, GetCashierRes{
ID: cashier.ID,
FirstName: cashier.FirstName,
LastName: cashier.LastName,
Email: cashier.Email,
PhoneNumber: cashier.PhoneNumber,
Role: cashier.Role,
EmailVerified: cashier.EmailVerified,
PhoneVerified: cashier.PhoneVerified,
CreatedAt: cashier.CreatedAt,
UpdatedAt: cashier.UpdatedAt,
SuspendedAt: cashier.SuspendedAt,
Suspended: cashier.Suspended,
LastLogin: *lastLogin,
})
}
return response.WriteJSON(c, fiber.StatusOK, "Branch Cashiers retrieved successfully", result, nil)
}
// GetBetByBranchID godoc // GetBetByBranchID godoc
// @Summary Gets bets by its branch id // @Summary Gets bets by its branch id
// @Description Gets bets by its branch id // @Description Gets bets by its branch id

View File

@ -25,12 +25,15 @@ type CompanyRes struct {
} }
type GetCompanyRes struct { type GetCompanyRes struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
Name string `json:"name" example:"CompanyName"` Name string `json:"name" example:"CompanyName"`
AdminID int64 `json:"admin_id" example:"1"` AdminID int64 `json:"admin_id" example:"1"`
WalletID int64 `json:"wallet_id" example:"1"` WalletID int64 `json:"wallet_id" example:"1"`
WalletBalance float32 `json:"balance" example:"1"` WalletBalance float32 `json:"balance" example:"1"`
IsActive bool `json:"is_active" example:"false"` IsActive bool `json:"is_active" example:"false"`
AdminFirstName string `json:"admin_first_name" example:"John"`
AdminLastName string `json:"admin_last_name" example:"Doe"`
AdminPhoneNumber string `json:"admin_phone_number" example:"1234567890"`
} }
func convertCompany(company domain.Company) CompanyRes { func convertCompany(company domain.Company) CompanyRes {
@ -44,12 +47,15 @@ func convertCompany(company domain.Company) CompanyRes {
func convertGetCompany(company domain.GetCompany) GetCompanyRes { func convertGetCompany(company domain.GetCompany) GetCompanyRes {
return GetCompanyRes{ return GetCompanyRes{
ID: company.ID, ID: company.ID,
Name: company.Name, Name: company.Name,
AdminID: company.AdminID, AdminID: company.AdminID,
WalletID: company.WalletID, WalletID: company.WalletID,
WalletBalance: company.WalletBalance.Float32(), WalletBalance: company.WalletBalance.Float32(),
IsActive: company.IsWalletActive, IsActive: company.IsWalletActive,
AdminFirstName: company.AdminFirstName,
AdminLastName: company.AdminLastName,
AdminPhoneNumber: company.AdminPhoneNumber,
} }
} }

View File

@ -182,7 +182,7 @@ func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
ticket, err := h.ticketSvc.GetTicketByID(c.Context(), id) ticket, err := h.ticketSvc.GetTicketByID(c.Context(), id)
if err != nil { if err != nil {
// h.logger.Error("Failed to get ticket by ID", "ticketID", id, "error", err) h.logger.Error("Failed to get ticket by ID", "ticketID", id, "error", err)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket") return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
} }

View File

@ -450,7 +450,7 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param id path int true "User ID" // @Param id path int true "User ID"
// @Success 200 {object} response.APIResponse // @Success 200 {object} UserProfileRes
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse // @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
@ -513,3 +513,75 @@ func (h *Handler) GetUserByID(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Cashiers retrieved successfully", res, nil) return response.WriteJSON(c, fiber.StatusOK, "Cashiers retrieved successfully", res, nil)
} }
// DeleteUser godoc
// @Summary Delete user by ID
// @Description Delete a user by their ID
// @Tags user
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /user/delete/{id} [delete]
func (h *Handler) DeleteUser(c *fiber.Ctx) error {
userIDstr := c.Params("id")
userID, err := strconv.ParseInt(userIDstr, 10, 64)
if err != nil {
h.logger.Error("DeleteUser failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid user ID", nil, nil)
}
err = h.userSvc.DeleteUser(c.Context(), userID)
if err != nil {
h.logger.Error("Failed to delete user", "userID", userID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to delete user", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "User deleted successfully", nil, nil)
}
type UpdateUserSuspendReq struct {
UserID int64 `json:"user_id" validate:"required" example:"123"`
Suspended bool `json:"suspended" validate:"required" example:"true"`
}
type UpdateUserSuspendRes struct {
UserID int64 `json:"user_id"`
Suspended bool `json:"suspended"`
}
// UpdateUserSuspend godoc
// @Summary Suspend or unsuspend a user
// @Description Suspend or unsuspend a user
// @Tags user
// @Accept json
// @Produce json
// @Param updateUserSuspend body UpdateUserSuspendReq true "Suspend or unsuspend a user"
// @Success 200 {object} UpdateUserSuspendRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /user/suspend [post]
func (h *Handler) UpdateUserSuspend(c *fiber.Ctx) error {
var req UpdateUserSuspendReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("Failed to parse UpdateUserSuspend request", "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
}
if valErrs, ok := h.validator.Validate(c, req); !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
err := h.userSvc.UpdateUserSuspend(c.Context(), req.UserID, req.Suspended)
if err != nil {
h.logger.Error("Failed to update user suspend status", "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update user suspend status")
}
res := UpdateUserSuspendRes{
UserID: req.UserID,
Suspended: req.Suspended,
}
return response.WriteJSON(c, fiber.StatusOK, "User suspend status updated successfully", res, nil)
}

View File

@ -35,7 +35,7 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/", func(c *fiber.Ctx) error { a.fiber.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{ return c.JSON(fiber.Map{
"message": "Welcome to the FortuneBet API", "message": "Welcome to the FortuneBet API",
"version": "1.0.1", "version": "1.0dev2",
}) })
}) })
@ -77,6 +77,8 @@ func (a *App) initAppRoutes() {
a.fiber.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist) a.fiber.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist)
a.fiber.Get("/user/profile", a.authMiddleware, h.UserProfile) a.fiber.Get("/user/profile", a.authMiddleware, h.UserProfile)
a.fiber.Get("/user/single/:id", a.authMiddleware, h.GetUserByID) a.fiber.Get("/user/single/:id", a.authMiddleware, h.GetUserByID)
a.fiber.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser)
a.fiber.Post("/user/suspend", a.authMiddleware, h.UpdateUserSuspend)
a.fiber.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet) a.fiber.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet)
a.fiber.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone) a.fiber.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone)
@ -120,12 +122,14 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/search/branch", a.authMiddleware, h.SearchBranch) a.fiber.Get("/search/branch", a.authMiddleware, h.SearchBranch)
// /branch/search // /branch/search
// branch/wallet // branch/wallet
a.fiber.Get("/branch/:id/cashiers", a.authMiddleware, h.GetBranchCashiers)
// Branch Operation // Branch Operation
a.fiber.Get("/supportedOperation", a.authMiddleware, h.GetAllSupportedOperations) a.fiber.Get("/supportedOperation", a.authMiddleware, h.GetAllSupportedOperations)
a.fiber.Post("/supportedOperation", a.authMiddleware, h.CreateSupportedOperation) a.fiber.Post("/supportedOperation", a.authMiddleware, h.CreateSupportedOperation)
a.fiber.Post("/operation", a.authMiddleware, h.CreateBranchOperation) a.fiber.Post("/operation", a.authMiddleware, h.CreateBranchOperation)
a.fiber.Get("/branch/:id/operation", a.authMiddleware, h.GetBranchOperations) a.fiber.Get("/branch/:id/operation", a.authMiddleware, h.GetBranchOperations)
a.fiber.Delete("/branch/:id/operation/:opID", a.authMiddleware, h.DeleteBranchOperation) a.fiber.Delete("/branch/:id/operation/:opID", a.authMiddleware, h.DeleteBranchOperation)
// Company // Company
@ -145,7 +149,7 @@ func (a *App) initAppRoutes() {
// Bet Routes // Bet Routes
a.fiber.Post("/bet", a.authMiddleware, h.CreateBet) a.fiber.Post("/bet", a.authMiddleware, h.CreateBet)
a.fiber.Get("/bet", a.authMiddleware, h.GetAllBet) a.fiber.Get("/bet", a.authMiddleware, h.GetAllBet)
a.fiber.Get("/bet/:id", a.authMiddleware, h.GetBetByID) a.fiber.Get("/bet/:id", h.GetBetByID)
a.fiber.Get("/bet/cashout/:id", a.authMiddleware, h.GetBetByCashoutID) a.fiber.Get("/bet/cashout/:id", a.authMiddleware, h.GetBetByCashoutID)
a.fiber.Patch("/bet/:id", a.authMiddleware, h.UpdateCashOut) a.fiber.Patch("/bet/:id", a.authMiddleware, h.UpdateCashOut)
a.fiber.Delete("/bet/:id", a.authMiddleware, h.DeleteBet) a.fiber.Delete("/bet/:id", a.authMiddleware, h.DeleteBet)