This commit is contained in:
Samuel Tariku 2025-04-14 07:59:23 +03:00
parent 788119f718
commit b4a21a5ddb
23 changed files with 1100 additions and 372 deletions

View File

@ -56,11 +56,12 @@ CREATE TABLE IF NOT EXISTS bets (
CHECK ( CHECK (
user_id IS NOT NULL user_id IS NOT NULL
OR branch_id IS NOT NULL OR branch_id IS NOT NULL
) ),
UNIQUE(cashier_id)
); );
CREATE TABLE IF NOT EXISTS tickets ( CREATE TABLE IF NOT EXISTS tickets (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
amount BIGINT NULL, amount BIGINT NOT NULL,
total_odds REAL NOT NULL, total_odds REAL NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
@ -68,14 +69,28 @@ CREATE TABLE IF NOT EXISTS tickets (
CREATE TABLE IF NOT EXISTS bet_outcomes ( CREATE TABLE IF NOT EXISTS bet_outcomes (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
bet_id BIGINT NOT NULL, bet_id BIGINT NOT NULL,
event_id bigint not null, event_id BIGINT NOT null,
odd_id BIGINT NOT NULL odd_id BIGINT NOT NULL,
home_team_name VARCHAR(255) NOT NULL,
away_team_name VARCHAR(255) NOT NULL,
market_id BIGINT NOT NULL,
market_name VARCHAR(255) NOT NULL,
odd REAL NOT NULL,
odd_name VARCHAR(255) NOT NULL,
expires TIMESTAMP NOT NULL
); );
CREATE TABLE IF NOT EXISTS ticket_outcomes ( CREATE TABLE IF NOT EXISTS ticket_outcomes (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
ticket_id BIGINT NOT NULL, ticket_id BIGINT NOT NULL,
event_id bigint not null, event_id BIGINT NOT null,
odd_id BIGINT NOT NULL odd_id BIGINT NOT NULL,
home_team_name VARCHAR(255) NOT NULL,
away_team_name VARCHAR(255) NOT NULL,
market_id BIGINT NOT NULL,
market_name VARCHAR(255) NOT NULL,
odd REAL NOT NULL,
odd_name VARCHAR(255) NOT NULL,
expires TIMESTAMP NOT NULL
); );
CREATE VIEW bet_with_outcomes AS CREATE VIEW bet_with_outcomes AS
SELECT bets.*, SELECT bets.*,

View File

@ -13,8 +13,19 @@ INSERT INTO bets (
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING *; RETURNING *;
-- name: CreateBetOutcome :copyfrom -- name: CreateBetOutcome :copyfrom
INSERT INTO bet_outcomes (bet_id, event_id, odd_id) INSERT INTO bet_outcomes (
VALUES ($1, $2, $3); bet_id,
event_id,
odd_id,
home_team_name,
away_team_name,
market_id,
market_name,
odd,
odd_name,
expires
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
-- name: GetAllBets :many -- name: GetAllBets :many
SELECT * SELECT *
FROM bet_with_outcomes; FROM bet_with_outcomes;
@ -22,8 +33,8 @@ FROM bet_with_outcomes;
SELECT * SELECT *
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE id = $1; WHERE id = $1;
-- name: GetBetByCashoutID :many -- name: GetBetByCashoutID :one
SELECT SELECT *
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE cashout_id = $1; WHERE cashout_id = $1;
-- name: GetBetByBranchID :many -- name: GetBetByBranchID :many

View File

@ -3,8 +3,19 @@ INSERT INTO tickets (amount, total_odds)
VALUES ($1, $2) VALUES ($1, $2)
RETURNING *; RETURNING *;
-- name: CreateTicketOutcome :copyfrom -- name: CreateTicketOutcome :copyfrom
INSERT INTO ticket_outcomes (ticket_id, event_id, odd_id) INSERT INTO ticket_outcomes (
VALUES ($1, $2, $3); ticket_id,
event_id,
odd_id,
home_team_name,
away_team_name,
market_id,
market_name,
odd,
odd_name,
expires
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
-- name: GetAllTickets :many -- name: GetAllTickets :many
SELECT * SELECT *
FROM ticket_with_outcomes; FROM ticket_with_outcomes;

View File

@ -262,6 +262,50 @@ const docTemplate = `{
} }
} }
}, },
"/bet/cashout/{id}": {
"get": {
"description": "Gets a single bet by cashout id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"bet"
],
"summary": "Gets bet by cashout id",
"parameters": [
{
"type": "string",
"description": "cashout ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BetRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/bet/{id}": { "/bet/{id}": {
"get": { "get": {
"description": "Gets a single bet by id", "description": "Gets a single bet by id",
@ -2577,17 +2621,49 @@ const docTemplate = `{
"domain.BetOutcome": { "domain.BetOutcome": {
"type": "object", "type": "object",
"properties": { "properties": {
"betID": { "away_team_name": {
"type": "integer" "type": "string",
"example": "Liverpool"
}, },
"eventID": { "bet_id": {
"type": "integer" "type": "integer",
"example": 1
},
"event_id": {
"type": "integer",
"example": 1
},
"expires": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"home_team_name": {
"type": "string",
"example": "Manchester"
}, },
"id": { "id": {
"type": "integer" "type": "integer",
"example": 1
}, },
"oddID": { "market_id": {
"type": "integer" "type": "integer",
"example": 1
},
"market_name": {
"type": "string",
"example": "Fulltime Result"
},
"odd": {
"type": "number",
"example": 1.5
},
"odd_id": {
"type": "integer",
"example": 1
},
"odd_name": {
"type": "string",
"example": "1"
} }
} }
}, },
@ -2710,18 +2786,46 @@ const docTemplate = `{
"domain.TicketOutcome": { "domain.TicketOutcome": {
"type": "object", "type": "object",
"properties": { "properties": {
"away_team_name": {
"type": "string",
"example": "Liverpool"
},
"event_id": { "event_id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
}, },
"expires": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"home_team_name": {
"type": "string",
"example": "Manchester"
},
"id": { "id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
}, },
"market_id": {
"type": "integer",
"example": 1
},
"market_name": {
"type": "string",
"example": "Fulltime Result"
},
"odd": {
"type": "number",
"example": 1.5
},
"odd_id": { "odd_id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
}, },
"odd_name": {
"type": "string",
"example": "1"
},
"ticket_id": { "ticket_id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
@ -2785,19 +2889,6 @@ const docTemplate = `{
} }
} }
}, },
"handlers.BetOutcome": {
"type": "object",
"properties": {
"event_id": {
"type": "integer",
"example": 1
},
"odd_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.BetRes": { "handlers.BetRes": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -2809,6 +2900,14 @@ const docTemplate = `{
"type": "integer", "type": "integer",
"example": 2 "example": 2
}, },
"cashed_id": {
"type": "string",
"example": "21234"
},
"cashed_out": {
"type": "boolean",
"example": false
},
"full_name": { "full_name": {
"type": "string", "type": "string",
"example": "John" "example": "John"
@ -2960,6 +3059,51 @@ const docTemplate = `{
} }
} }
}, },
"handlers.CreateBetOutcomeReq": {
"type": "object",
"properties": {
"away_team_name": {
"type": "string",
"example": "Liverpool"
},
"bet_id": {
"type": "integer",
"example": 1
},
"event_id": {
"type": "integer",
"example": 1
},
"expires": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"home_team_name": {
"type": "string",
"example": "Manchester"
},
"market_id": {
"type": "integer",
"example": 1
},
"market_name": {
"type": "string",
"example": "Fulltime Result"
},
"odd": {
"type": "number",
"example": 1.5
},
"odd_id": {
"type": "integer",
"example": 1
},
"odd_name": {
"type": "string",
"example": "1"
}
}
},
"handlers.CreateBetReq": { "handlers.CreateBetReq": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -2978,7 +3122,7 @@ const docTemplate = `{
"outcomes": { "outcomes": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/handlers.BetOutcome" "$ref": "#/definitions/handlers.CreateBetOutcomeReq"
} }
}, },
"phone_number": { "phone_number": {
@ -3110,6 +3254,51 @@ const docTemplate = `{
} }
} }
}, },
"handlers.CreateTicketOutcomeReq": {
"type": "object",
"properties": {
"away_team_name": {
"type": "string",
"example": "Liverpool"
},
"event_id": {
"type": "integer",
"example": 1
},
"expires": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"home_team_name": {
"type": "string",
"example": "Manchester"
},
"market_id": {
"type": "integer",
"example": 1
},
"market_name": {
"type": "string",
"example": "Fulltime Result"
},
"odd": {
"type": "number",
"example": 1.5
},
"odd_id": {
"type": "integer",
"example": 1
},
"odd_name": {
"type": "string",
"example": "1"
},
"ticket_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.CreateTicketReq": { "handlers.CreateTicketReq": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -3120,7 +3309,7 @@ const docTemplate = `{
"outcomes": { "outcomes": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/handlers.TicketOutcome" "$ref": "#/definitions/handlers.CreateTicketOutcomeReq"
} }
}, },
"total_odds": { "total_odds": {
@ -3166,14 +3355,6 @@ const docTemplate = `{
"type": "integer", "type": "integer",
"example": 1 "example": 1
}, },
"branch_id": {
"type": "integer",
"example": 1
},
"cashier_id": {
"type": "integer",
"example": 1
},
"full_name": { "full_name": {
"type": "string", "type": "string",
"example": "John Smith" "example": "John Smith"
@ -3356,19 +3537,6 @@ const docTemplate = `{
} }
} }
}, },
"handlers.TicketOutcome": {
"type": "object",
"properties": {
"event_id": {
"type": "integer",
"example": 1
},
"odd_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.TicketRes": { "handlers.TicketRes": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -254,6 +254,50 @@
} }
} }
}, },
"/bet/cashout/{id}": {
"get": {
"description": "Gets a single bet by cashout id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"bet"
],
"summary": "Gets bet by cashout id",
"parameters": [
{
"type": "string",
"description": "cashout ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.BetRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/bet/{id}": { "/bet/{id}": {
"get": { "get": {
"description": "Gets a single bet by id", "description": "Gets a single bet by id",
@ -2569,17 +2613,49 @@
"domain.BetOutcome": { "domain.BetOutcome": {
"type": "object", "type": "object",
"properties": { "properties": {
"betID": { "away_team_name": {
"type": "integer" "type": "string",
"example": "Liverpool"
}, },
"eventID": { "bet_id": {
"type": "integer" "type": "integer",
"example": 1
},
"event_id": {
"type": "integer",
"example": 1
},
"expires": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"home_team_name": {
"type": "string",
"example": "Manchester"
}, },
"id": { "id": {
"type": "integer" "type": "integer",
"example": 1
}, },
"oddID": { "market_id": {
"type": "integer" "type": "integer",
"example": 1
},
"market_name": {
"type": "string",
"example": "Fulltime Result"
},
"odd": {
"type": "number",
"example": 1.5
},
"odd_id": {
"type": "integer",
"example": 1
},
"odd_name": {
"type": "string",
"example": "1"
} }
} }
}, },
@ -2702,18 +2778,46 @@
"domain.TicketOutcome": { "domain.TicketOutcome": {
"type": "object", "type": "object",
"properties": { "properties": {
"away_team_name": {
"type": "string",
"example": "Liverpool"
},
"event_id": { "event_id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
}, },
"expires": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"home_team_name": {
"type": "string",
"example": "Manchester"
},
"id": { "id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
}, },
"market_id": {
"type": "integer",
"example": 1
},
"market_name": {
"type": "string",
"example": "Fulltime Result"
},
"odd": {
"type": "number",
"example": 1.5
},
"odd_id": { "odd_id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
}, },
"odd_name": {
"type": "string",
"example": "1"
},
"ticket_id": { "ticket_id": {
"type": "integer", "type": "integer",
"example": 1 "example": 1
@ -2777,19 +2881,6 @@
} }
} }
}, },
"handlers.BetOutcome": {
"type": "object",
"properties": {
"event_id": {
"type": "integer",
"example": 1
},
"odd_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.BetRes": { "handlers.BetRes": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -2801,6 +2892,14 @@
"type": "integer", "type": "integer",
"example": 2 "example": 2
}, },
"cashed_id": {
"type": "string",
"example": "21234"
},
"cashed_out": {
"type": "boolean",
"example": false
},
"full_name": { "full_name": {
"type": "string", "type": "string",
"example": "John" "example": "John"
@ -2952,6 +3051,51 @@
} }
} }
}, },
"handlers.CreateBetOutcomeReq": {
"type": "object",
"properties": {
"away_team_name": {
"type": "string",
"example": "Liverpool"
},
"bet_id": {
"type": "integer",
"example": 1
},
"event_id": {
"type": "integer",
"example": 1
},
"expires": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"home_team_name": {
"type": "string",
"example": "Manchester"
},
"market_id": {
"type": "integer",
"example": 1
},
"market_name": {
"type": "string",
"example": "Fulltime Result"
},
"odd": {
"type": "number",
"example": 1.5
},
"odd_id": {
"type": "integer",
"example": 1
},
"odd_name": {
"type": "string",
"example": "1"
}
}
},
"handlers.CreateBetReq": { "handlers.CreateBetReq": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -2970,7 +3114,7 @@
"outcomes": { "outcomes": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/handlers.BetOutcome" "$ref": "#/definitions/handlers.CreateBetOutcomeReq"
} }
}, },
"phone_number": { "phone_number": {
@ -3102,6 +3246,51 @@
} }
} }
}, },
"handlers.CreateTicketOutcomeReq": {
"type": "object",
"properties": {
"away_team_name": {
"type": "string",
"example": "Liverpool"
},
"event_id": {
"type": "integer",
"example": 1
},
"expires": {
"type": "string",
"example": "2025-04-08T12:00:00Z"
},
"home_team_name": {
"type": "string",
"example": "Manchester"
},
"market_id": {
"type": "integer",
"example": 1
},
"market_name": {
"type": "string",
"example": "Fulltime Result"
},
"odd": {
"type": "number",
"example": 1.5
},
"odd_id": {
"type": "integer",
"example": 1
},
"odd_name": {
"type": "string",
"example": "1"
},
"ticket_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.CreateTicketReq": { "handlers.CreateTicketReq": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -3112,7 +3301,7 @@
"outcomes": { "outcomes": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/handlers.TicketOutcome" "$ref": "#/definitions/handlers.CreateTicketOutcomeReq"
} }
}, },
"total_odds": { "total_odds": {
@ -3158,14 +3347,6 @@
"type": "integer", "type": "integer",
"example": 1 "example": 1
}, },
"branch_id": {
"type": "integer",
"example": 1
},
"cashier_id": {
"type": "integer",
"example": 1
},
"full_name": { "full_name": {
"type": "string", "type": "string",
"example": "John Smith" "example": "John Smith"
@ -3348,19 +3529,6 @@
} }
} }
}, },
"handlers.TicketOutcome": {
"type": "object",
"properties": {
"event_id": {
"type": "integer",
"example": 1
},
"odd_id": {
"type": "integer",
"example": 1
}
}
},
"handlers.TicketRes": { "handlers.TicketRes": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -1,14 +1,39 @@
definitions: definitions:
domain.BetOutcome: domain.BetOutcome:
properties: properties:
betID: away_team_name:
example: Liverpool
type: string
bet_id:
example: 1
type: integer type: integer
eventID: event_id:
example: 1
type: integer type: integer
expires:
example: "2025-04-08T12:00:00Z"
type: string
home_team_name:
example: Manchester
type: string
id: id:
example: 1
type: integer type: integer
oddID: market_id:
example: 1
type: integer type: integer
market_name:
example: Fulltime Result
type: string
odd:
example: 1.5
type: number
odd_id:
example: 1
type: integer
odd_name:
example: "1"
type: string
type: object type: object
domain.BetStatus: domain.BetStatus:
enum: enum:
@ -96,15 +121,36 @@ definitions:
- RoleCashier - RoleCashier
domain.TicketOutcome: domain.TicketOutcome:
properties: properties:
away_team_name:
example: Liverpool
type: string
event_id: event_id:
example: 1 example: 1
type: integer type: integer
expires:
example: "2025-04-08T12:00:00Z"
type: string
home_team_name:
example: Manchester
type: string
id: id:
example: 1 example: 1
type: integer type: integer
market_id:
example: 1
type: integer
market_name:
example: Fulltime Result
type: string
odd:
example: 1.5
type: number
odd_id: odd_id:
example: 1 example: 1
type: integer type: integer
odd_name:
example: "1"
type: string
ticket_id: ticket_id:
example: 1 example: 1
type: integer type: integer
@ -151,15 +197,6 @@ definitions:
description: Converted from "time" field in UNIX format description: Converted from "time" field in UNIX format
type: string type: string
type: object type: object
handlers.BetOutcome:
properties:
event_id:
example: 1
type: integer
odd_id:
example: 1
type: integer
type: object
handlers.BetRes: handlers.BetRes:
properties: properties:
amount: amount:
@ -168,6 +205,12 @@ definitions:
branch_id: branch_id:
example: 2 example: 2
type: integer type: integer
cashed_id:
example: "21234"
type: string
cashed_out:
example: false
type: boolean
full_name: full_name:
example: John example: John
type: string type: string
@ -274,6 +317,39 @@ definitions:
phone_number_exist: phone_number_exist:
type: boolean type: boolean
type: object type: object
handlers.CreateBetOutcomeReq:
properties:
away_team_name:
example: Liverpool
type: string
bet_id:
example: 1
type: integer
event_id:
example: 1
type: integer
expires:
example: "2025-04-08T12:00:00Z"
type: string
home_team_name:
example: Manchester
type: string
market_id:
example: 1
type: integer
market_name:
example: Fulltime Result
type: string
odd:
example: 1.5
type: number
odd_id:
example: 1
type: integer
odd_name:
example: "1"
type: string
type: object
handlers.CreateBetReq: handlers.CreateBetReq:
properties: properties:
amount: amount:
@ -287,7 +363,7 @@ definitions:
type: boolean type: boolean
outcomes: outcomes:
items: items:
$ref: '#/definitions/handlers.BetOutcome' $ref: '#/definitions/handlers.CreateBetOutcomeReq'
type: array type: array
phone_number: phone_number:
example: "1234567890" example: "1234567890"
@ -379,6 +455,39 @@ definitions:
example: SportsBook example: SportsBook
type: string type: string
type: object type: object
handlers.CreateTicketOutcomeReq:
properties:
away_team_name:
example: Liverpool
type: string
event_id:
example: 1
type: integer
expires:
example: "2025-04-08T12:00:00Z"
type: string
home_team_name:
example: Manchester
type: string
market_id:
example: 1
type: integer
market_name:
example: Fulltime Result
type: string
odd:
example: 1.5
type: number
odd_id:
example: 1
type: integer
odd_name:
example: "1"
type: string
ticket_id:
example: 1
type: integer
type: object
handlers.CreateTicketReq: handlers.CreateTicketReq:
properties: properties:
amount: amount:
@ -386,7 +495,7 @@ definitions:
type: number type: number
outcomes: outcomes:
items: items:
$ref: '#/definitions/handlers.TicketOutcome' $ref: '#/definitions/handlers.CreateTicketOutcomeReq'
type: array type: array
total_odds: total_odds:
example: 4.22 example: 4.22
@ -418,12 +527,6 @@ definitions:
bet_id: bet_id:
example: 1 example: 1
type: integer type: integer
branch_id:
example: 1
type: integer
cashier_id:
example: 1
type: integer
full_name: full_name:
example: John Smith example: John Smith
type: string type: string
@ -550,15 +653,6 @@ definitions:
example: SportsBook example: SportsBook
type: string type: string
type: object type: object
handlers.TicketOutcome:
properties:
event_id:
example: 1
type: integer
odd_id:
example: 1
type: integer
type: object
handlers.TicketRes: handlers.TicketRes:
properties: properties:
amount: amount:
@ -1046,6 +1140,35 @@ paths:
summary: Updates the cashed out field summary: Updates the cashed out field
tags: tags:
- bet - bet
/bet/cashout/{id}:
get:
consumes:
- application/json
description: Gets a single bet by cashout id
parameters:
- description: cashout ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.BetRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets bet by cashout id
tags:
- bet
/branch: /branch:
get: get:
consumes: consumes:

View File

@ -71,9 +71,16 @@ func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, erro
} }
type CreateBetOutcomeParams struct { type CreateBetOutcomeParams struct {
BetID int64 `json:"bet_id"` BetID int64 `json:"bet_id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
OddID int64 `json:"odd_id"` OddID int64 `json:"odd_id"`
HomeTeamName string `json:"home_team_name"`
AwayTeamName string `json:"away_team_name"`
MarketID int64 `json:"market_id"`
MarketName string `json:"market_name"`
Odd float32 `json:"odd"`
OddName string `json:"odd_name"`
Expires pgtype.Timestamp `json:"expires"`
} }
const DeleteBet = `-- name: DeleteBet :exec const DeleteBet = `-- name: DeleteBet :exec
@ -177,33 +184,32 @@ func (q *Queries) GetBetByBranchID(ctx context.Context, branchID pgtype.Int8) ([
return items, nil return items, nil
} }
const GetBetByCashoutID = `-- name: GetBetByCashoutID :many const GetBetByCashoutID = `-- name: GetBetByCashoutID :one
SELECT SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE cashout_id = $1 WHERE cashout_id = $1
` `
type GetBetByCashoutIDRow struct { func (q *Queries) GetBetByCashoutID(ctx context.Context, cashoutID string) (BetWithOutcome, error) {
} row := q.db.QueryRow(ctx, GetBetByCashoutID, cashoutID)
var i BetWithOutcome
func (q *Queries) GetBetByCashoutID(ctx context.Context, cashoutID string) ([]GetBetByCashoutIDRow, error) { err := row.Scan(
rows, err := q.db.Query(ctx, GetBetByCashoutID, cashoutID) &i.ID,
if err != nil { &i.Amount,
return nil, err &i.TotalOdds,
} &i.Status,
defer rows.Close() &i.FullName,
var items []GetBetByCashoutIDRow &i.PhoneNumber,
for rows.Next() { &i.BranchID,
var i GetBetByCashoutIDRow &i.UserID,
if err := rows.Scan(); err != nil { &i.CashedOut,
return nil, err &i.CashoutID,
} &i.CreatedAt,
items = append(items, i) &i.UpdatedAt,
} &i.IsShopBet,
if err := rows.Err(); err != nil { &i.Outcomes,
return nil, err )
} return i, err
return items, nil
} }
const GetBetByID = `-- name: GetBetByID :one const GetBetByID = `-- name: GetBetByID :one

View File

@ -32,6 +32,13 @@ func (r iteratorForCreateBetOutcome) Values() ([]interface{}, error) {
r.rows[0].BetID, r.rows[0].BetID,
r.rows[0].EventID, r.rows[0].EventID,
r.rows[0].OddID, r.rows[0].OddID,
r.rows[0].HomeTeamName,
r.rows[0].AwayTeamName,
r.rows[0].MarketID,
r.rows[0].MarketName,
r.rows[0].Odd,
r.rows[0].OddName,
r.rows[0].Expires,
}, nil }, nil
} }
@ -40,7 +47,7 @@ func (r iteratorForCreateBetOutcome) Err() error {
} }
func (q *Queries) CreateBetOutcome(ctx context.Context, arg []CreateBetOutcomeParams) (int64, error) { func (q *Queries) CreateBetOutcome(ctx context.Context, arg []CreateBetOutcomeParams) (int64, error) {
return q.db.CopyFrom(ctx, []string{"bet_outcomes"}, []string{"bet_id", "event_id", "odd_id"}, &iteratorForCreateBetOutcome{rows: arg}) return q.db.CopyFrom(ctx, []string{"bet_outcomes"}, []string{"bet_id", "event_id", "odd_id", "home_team_name", "away_team_name", "market_id", "market_name", "odd", "odd_name", "expires"}, &iteratorForCreateBetOutcome{rows: arg})
} }
// iteratorForCreateTicketOutcome implements pgx.CopyFromSource. // iteratorForCreateTicketOutcome implements pgx.CopyFromSource.
@ -66,6 +73,13 @@ func (r iteratorForCreateTicketOutcome) Values() ([]interface{}, error) {
r.rows[0].TicketID, r.rows[0].TicketID,
r.rows[0].EventID, r.rows[0].EventID,
r.rows[0].OddID, r.rows[0].OddID,
r.rows[0].HomeTeamName,
r.rows[0].AwayTeamName,
r.rows[0].MarketID,
r.rows[0].MarketName,
r.rows[0].Odd,
r.rows[0].OddName,
r.rows[0].Expires,
}, nil }, nil
} }
@ -74,5 +88,5 @@ func (r iteratorForCreateTicketOutcome) Err() error {
} }
func (q *Queries) CreateTicketOutcome(ctx context.Context, arg []CreateTicketOutcomeParams) (int64, error) { func (q *Queries) CreateTicketOutcome(ctx context.Context, arg []CreateTicketOutcomeParams) (int64, error) {
return q.db.CopyFrom(ctx, []string{"ticket_outcomes"}, []string{"ticket_id", "event_id", "odd_id"}, &iteratorForCreateTicketOutcome{rows: arg}) return q.db.CopyFrom(ctx, []string{"ticket_outcomes"}, []string{"ticket_id", "event_id", "odd_id", "home_team_name", "away_team_name", "market_id", "market_name", "odd", "odd_name", "expires"}, &iteratorForCreateTicketOutcome{rows: arg})
} }

View File

@ -25,10 +25,17 @@ type Bet struct {
} }
type BetOutcome struct { type BetOutcome struct {
ID int64 `json:"id"` ID int64 `json:"id"`
BetID int64 `json:"bet_id"` BetID int64 `json:"bet_id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
OddID int64 `json:"odd_id"` OddID int64 `json:"odd_id"`
HomeTeamName string `json:"home_team_name"`
AwayTeamName string `json:"away_team_name"`
MarketID int64 `json:"market_id"`
MarketName string `json:"market_name"`
Odd float32 `json:"odd"`
OddName string `json:"odd_name"`
Expires pgtype.Timestamp `json:"expires"`
} }
type BetWithOutcome struct { type BetWithOutcome struct {
@ -148,7 +155,6 @@ type Odd struct {
MarketCategory pgtype.Text `json:"market_category"` MarketCategory pgtype.Text `json:"market_category"`
MarketID pgtype.Text `json:"market_id"` MarketID pgtype.Text `json:"market_id"`
Name pgtype.Text `json:"name"` Name pgtype.Text `json:"name"`
Header pgtype.Text `json:"header"`
Handicap pgtype.Text `json:"handicap"` Handicap pgtype.Text `json:"handicap"`
OddsValue pgtype.Float8 `json:"odds_value"` OddsValue pgtype.Float8 `json:"odds_value"`
Section string `json:"section"` Section string `json:"section"`
@ -188,22 +194,29 @@ type SupportedOperation struct {
type Ticket struct { type Ticket struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Amount pgtype.Int8 `json:"amount"` Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"` TotalOdds float32 `json:"total_odds"`
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
type TicketOutcome struct { type TicketOutcome struct {
ID int64 `json:"id"` ID int64 `json:"id"`
TicketID int64 `json:"ticket_id"` TicketID int64 `json:"ticket_id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
OddID int64 `json:"odd_id"` OddID int64 `json:"odd_id"`
HomeTeamName string `json:"home_team_name"`
AwayTeamName string `json:"away_team_name"`
MarketID int64 `json:"market_id"`
MarketName string `json:"market_name"`
Odd float32 `json:"odd"`
OddName string `json:"odd_name"`
Expires pgtype.Timestamp `json:"expires"`
} }
type TicketWithOutcome struct { type TicketWithOutcome struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Amount pgtype.Int8 `json:"amount"` Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"` TotalOdds float32 `json:"total_odds"`
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"`

View File

@ -12,7 +12,8 @@ import (
) )
const GetALLPrematchOdds = `-- name: GetALLPrematchOdds :many const GetALLPrematchOdds = `-- name: GetALLPrematchOdds :many
SELECT event_id, SELECT
event_id,
fi, fi,
market_type, market_type,
market_name, market_name,
@ -28,8 +29,7 @@ SELECT event_id,
source, source,
is_active is_active
FROM odds FROM odds
WHERE is_active = true WHERE is_active = true AND source = 'b365api'
AND source = 'b365api'
` `
type GetALLPrematchOddsRow struct { type GetALLPrematchOddsRow struct {
@ -87,7 +87,8 @@ func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]GetALLPrematchOddsR
} }
const GetPrematchOdds = `-- name: GetPrematchOdds :many const GetPrematchOdds = `-- name: GetPrematchOdds :many
SELECT event_id, SELECT
event_id,
fi, fi,
market_type, market_type,
market_name, market_name,
@ -103,8 +104,7 @@ SELECT event_id,
source, source,
is_active is_active
FROM odds FROM odds
WHERE is_active = true WHERE is_active = true AND source = 'b365api'
AND source = 'b365api'
` `
type GetPrematchOddsRow struct { type GetPrematchOddsRow struct {
@ -162,7 +162,8 @@ func (q *Queries) GetPrematchOdds(ctx context.Context) ([]GetPrematchOddsRow, er
} }
const GetPrematchOddsByUpcomingID = `-- name: GetPrematchOddsByUpcomingID :many const GetPrematchOddsByUpcomingID = `-- name: GetPrematchOddsByUpcomingID :many
SELECT o.event_id, SELECT
o.event_id,
o.fi, o.fi,
o.market_type, o.market_type,
o.market_name, o.market_name,
@ -178,12 +179,12 @@ SELECT o.event_id,
o.source, o.source,
o.is_active o.is_active
FROM odds o FROM odds o
JOIN events e ON o.fi = e.id JOIN events e ON o.fi = e.id
WHERE e.id = $1 WHERE e.id = $1
AND e.is_live = false AND e.is_live = false
AND e.status = 'upcoming' AND e.status = 'upcoming'
AND o.is_active = true AND o.is_active = true
AND o.source = 'b365api' AND o.source = 'b365api'
LIMIT $2 OFFSET $3 LIMIT $2 OFFSET $3
` `
@ -248,13 +249,15 @@ func (q *Queries) GetPrematchOddsByUpcomingID(ctx context.Context, arg GetPremat
} }
const GetRawOddsByID = `-- name: GetRawOddsByID :one const GetRawOddsByID = `-- name: GetRawOddsByID :one
SELECT id, SELECT
raw_odds, id,
raw_odds,
fetched_at fetched_at
FROM odds FROM odds
WHERE raw_odds @> $1::jsonb WHERE
AND is_active = true raw_odds @> $1::jsonb AND
AND source = 'b365api' is_active = true AND
source = 'b365api'
LIMIT 1 LIMIT 1
` `
@ -273,49 +276,35 @@ func (q *Queries) GetRawOddsByID(ctx context.Context, dollar_1 []byte) (GetRawOd
const InsertNonLiveOdd = `-- name: InsertNonLiveOdd :exec const InsertNonLiveOdd = `-- name: InsertNonLiveOdd :exec
INSERT INTO odds ( INSERT INTO odds (
event_id, event_id,
fi, fi,
market_type, market_type,
market_name, market_name,
market_category, market_category,
market_id, market_id,
name, name,
handicap, handicap,
odds_value, odds_value,
section, section,
category, category,
raw_odds, raw_odds,
is_active, is_active,
source, source,
fetched_at fetched_at
) ) VALUES (
VALUES ( $1, $2, $3, $4, $5, $6, $7,
$1, $8, $9, $10, $11, $12, $13, $14, $15
$2, )
$3, ON CONFLICT (market_id, name, handicap) DO UPDATE SET
$4, odds_value = EXCLUDED.odds_value,
$5, raw_odds = EXCLUDED.raw_odds,
$6, market_type = EXCLUDED.market_type,
$7, market_name = EXCLUDED.market_name,
$8,
$9,
$10,
$11,
$12,
$13,
$14,
$15
) ON CONFLICT (market_id, name, handicap) DO
UPDATE
SET odds_value = EXCLUDED.odds_value,
raw_odds = EXCLUDED.raw_odds,
market_type = EXCLUDED.market_type,
market_name = EXCLUDED.market_name,
market_category = EXCLUDED.market_category, market_category = EXCLUDED.market_category,
fetched_at = EXCLUDED.fetched_at, fetched_at = EXCLUDED.fetched_at,
is_active = EXCLUDED.is_active, is_active = EXCLUDED.is_active,
source = EXCLUDED.source, source = EXCLUDED.source,
fi = EXCLUDED.fi fi = EXCLUDED.fi
` `
type InsertNonLiveOddParams struct { type InsertNonLiveOddParams struct {

View File

@ -18,8 +18,8 @@ RETURNING id, amount, total_odds, created_at, updated_at
` `
type CreateTicketParams struct { type CreateTicketParams struct {
Amount pgtype.Int8 `json:"amount"` Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"` TotalOdds float32 `json:"total_odds"`
} }
func (q *Queries) CreateTicket(ctx context.Context, arg CreateTicketParams) (Ticket, error) { func (q *Queries) CreateTicket(ctx context.Context, arg CreateTicketParams) (Ticket, error) {
@ -36,9 +36,16 @@ func (q *Queries) CreateTicket(ctx context.Context, arg CreateTicketParams) (Tic
} }
type CreateTicketOutcomeParams struct { type CreateTicketOutcomeParams struct {
TicketID int64 `json:"ticket_id"` TicketID int64 `json:"ticket_id"`
EventID int64 `json:"event_id"` EventID int64 `json:"event_id"`
OddID int64 `json:"odd_id"` OddID int64 `json:"odd_id"`
HomeTeamName string `json:"home_team_name"`
AwayTeamName string `json:"away_team_name"`
MarketID int64 `json:"market_id"`
MarketName string `json:"market_name"`
Odd float32 `json:"odd"`
OddName string `json:"odd_name"`
Expires pgtype.Timestamp `json:"expires"`
} }
const DeleteOldTickets = `-- name: DeleteOldTickets :exec const DeleteOldTickets = `-- name: DeleteOldTickets :exec
@ -124,7 +131,7 @@ func (q *Queries) GetTicketByID(ctx context.Context, id int64) (TicketWithOutcom
} }
const GetTicketOutcome = `-- name: GetTicketOutcome :many const GetTicketOutcome = `-- name: GetTicketOutcome :many
SELECT id, ticket_id, event_id, odd_id SELECT id, ticket_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, expires
FROM ticket_outcomes FROM ticket_outcomes
WHERE ticket_id = $1 WHERE ticket_id = $1
` `
@ -143,6 +150,13 @@ func (q *Queries) GetTicketOutcome(ctx context.Context, ticketID int64) ([]Ticke
&i.TicketID, &i.TicketID,
&i.EventID, &i.EventID,
&i.OddID, &i.OddID,
&i.HomeTeamName,
&i.AwayTeamName,
&i.MarketID,
&i.MarketName,
&i.Odd,
&i.OddName,
&i.Expires,
); err != nil { ); err != nil {
return nil, err return nil, err
} }

View File

@ -1,16 +1,32 @@
package domain package domain
import "time"
type BetOutcome struct { type BetOutcome struct {
ID int64 ID int64 `json:"id" example:"1"`
BetID int64 BetID int64 `json:"bet_id" example:"1"`
EventID int64 EventID int64 `json:"event_id" example:"1"`
OddID int64 OddID int64 `json:"odd_id" example:"1"`
HomeTeamName string `json:"home_team_name" example:"Manchester"`
AwayTeamName string `json:"away_team_name" example:"Liverpool"`
MarketID int64 `json:"market_id" example:"1"`
MarketName string `json:"market_name" example:"Fulltime Result"`
Odd float32 `json:"odd" example:"1.5"`
OddName string `json:"odd_name" example:"1"`
Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
} }
type CreateBetOutcome struct { type CreateBetOutcome struct {
BetID int64 BetID int64 `json:"bet_id" example:"1"`
EventID int64 EventID int64 `json:"event_id" example:"1"`
OddID int64 OddID int64 `json:"odd_id" example:"1"`
HomeTeamName string `json:"home_team_name" example:"Manchester"`
AwayTeamName string `json:"away_team_name" example:"Liverpool"`
MarketID int64 `json:"market_id" example:"1"`
MarketName string `json:"market_name" example:"Fulltime Result"`
Odd float32 `json:"odd" example:"1.5"`
OddName string `json:"odd_name" example:"1"`
Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
} }
type BetStatus int type BetStatus int

View File

@ -1,16 +1,32 @@
package domain package domain
import "time"
type TicketOutcome struct { type TicketOutcome struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
TicketID int64 `json:"ticket_id" example:"1"` TicketID int64 `json:"ticket_id" example:"1"`
EventID int64 `json:"event_id" example:"1"` EventID int64 `json:"event_id" example:"1"`
OddID int64 `json:"odd_id" example:"1"` OddID int64 `json:"odd_id" example:"1"`
HomeTeamName string `json:"home_team_name" example:"Manchester"`
AwayTeamName string `json:"away_team_name" example:"Liverpool"`
MarketID int64 `json:"market_id" example:"1"`
MarketName string `json:"market_name" example:"Fulltime Result"`
Odd float32 `json:"odd" example:"1.5"`
OddName string `json:"odd_name" example:"1"`
Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
} }
type CreateTicketOutcome struct { type CreateTicketOutcome struct {
TicketID int64 TicketID int64 `json:"ticket_id" example:"1"`
EventID int64 EventID int64 `json:"event_id" example:"1"`
OddID int64 OddID int64 `json:"odd_id" example:"1"`
HomeTeamName string `json:"home_team_name" example:"Manchester"`
AwayTeamName string `json:"away_team_name" example:"Liverpool"`
MarketID int64 `json:"market_id" example:"1"`
MarketName string `json:"market_name" example:"Fulltime Result"`
Odd float32 `json:"odd" example:"1.5"`
OddName string `json:"odd_name" example:"1"`
Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
} }
// ID will serve as the fast code since this doesn't need to be secure // ID will serve as the fast code since this doesn't need to be secure

View File

@ -2,6 +2,7 @@ package repository
import ( import (
"context" "context"
// "fmt"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -31,14 +32,21 @@ func convertDBBet(bet dbgen.Bet) domain.Bet {
} }
func convertDBBetOutcomes(bet dbgen.BetWithOutcome) domain.GetBet { func convertDBBetOutcomes(bet dbgen.BetWithOutcome) domain.GetBet {
var outcomes []domain.BetOutcome = make([]domain.BetOutcome, len(bet.Outcomes)) var outcomes []domain.BetOutcome = make([]domain.BetOutcome, 0, len(bet.Outcomes))
for _, outcome := range bet.Outcomes { for _, outcome := range bet.Outcomes {
outcomes = append(outcomes, domain.BetOutcome{ outcomes = append(outcomes, domain.BetOutcome{
ID: outcome.ID, ID: outcome.ID,
BetID: outcome.BetID, BetID: outcome.BetID,
EventID: outcome.EventID, EventID: outcome.EventID,
OddID: outcome.OddID, OddID: outcome.OddID,
HomeTeamName: outcome.HomeTeamName,
AwayTeamName: outcome.AwayTeamName,
MarketID: outcome.MarketID,
MarketName: outcome.MarketName,
Odd: outcome.Odd,
OddName: outcome.OddName,
Expires: outcome.Expires.Time,
}) })
} }
return domain.GetBet{ return domain.GetBet{
@ -63,6 +71,24 @@ func convertDBBetOutcomes(bet dbgen.BetWithOutcome) domain.GetBet {
} }
} }
func convertDBCreateBetOutcome(betOutcome domain.CreateBetOutcome) dbgen.CreateBetOutcomeParams {
return dbgen.CreateBetOutcomeParams{
BetID: betOutcome.BetID,
EventID: betOutcome.EventID,
OddID: betOutcome.OddID,
HomeTeamName: betOutcome.HomeTeamName,
AwayTeamName: betOutcome.AwayTeamName,
MarketID: betOutcome.MarketID,
MarketName: betOutcome.MarketName,
Odd: betOutcome.Odd,
OddName: betOutcome.OddName,
Expires: pgtype.Timestamp{
Time: betOutcome.Expires,
Valid: true,
},
}
}
func convertCreateBet(bet domain.CreateBet) dbgen.CreateBetParams { func convertCreateBet(bet domain.CreateBet) dbgen.CreateBetParams {
return dbgen.CreateBetParams{ return dbgen.CreateBetParams{
Amount: int64(bet.Amount), Amount: int64(bet.Amount),
@ -96,11 +122,7 @@ func (s *Store) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBe
var dbParams []dbgen.CreateBetOutcomeParams = make([]dbgen.CreateBetOutcomeParams, 0, len(outcomes)) var dbParams []dbgen.CreateBetOutcomeParams = make([]dbgen.CreateBetOutcomeParams, 0, len(outcomes))
for _, outcome := range outcomes { for _, outcome := range outcomes {
dbParams = append(dbParams, dbgen.CreateBetOutcomeParams{ dbParams = append(dbParams, convertDBCreateBetOutcome(outcome))
BetID: outcome.BetID,
EventID: outcome.EventID,
OddID: outcome.OddID,
})
} }
rows, err := s.queries.CreateBetOutcome(ctx, dbParams) rows, err := s.queries.CreateBetOutcome(ctx, dbParams)
@ -113,6 +135,17 @@ func (s *Store) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBe
func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) { func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) {
bet, err := s.queries.GetBetByID(ctx, id) bet, err := s.queries.GetBetByID(ctx, id)
if err != nil {
return domain.GetBet{}, err
}
return convertDBBetOutcomes(bet), nil
}
func (s *Store) GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error) {
bet, err := s.queries.GetBetByCashoutID(ctx, id)
if err != nil { if err != nil {
return domain.GetBet{}, err return domain.GetBet{}, err
} }
@ -122,7 +155,6 @@ func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error)
func (s *Store) GetAllBets(ctx context.Context) ([]domain.GetBet, error) { func (s *Store) GetAllBets(ctx context.Context) ([]domain.GetBet, error) {
bets, err := s.queries.GetAllBets(ctx) bets, err := s.queries.GetAllBets(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,7 +2,6 @@ package repository
import ( import (
"context" "context"
"fmt"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
@ -12,10 +11,9 @@ import (
func convertDBTicket(ticket dbgen.Ticket) domain.Ticket { func convertDBTicket(ticket dbgen.Ticket) domain.Ticket {
return domain.Ticket{ return domain.Ticket{
ID: ticket.ID, ID: ticket.ID,
Amount: domain.Currency(ticket.Amount.Int64), Amount: domain.Currency(ticket.Amount),
TotalOdds: ticket.TotalOdds, TotalOdds: ticket.TotalOdds,
} }
} }
func convertDBTicketOutcomes(ticket dbgen.TicketWithOutcome) domain.GetTicket { func convertDBTicketOutcomes(ticket dbgen.TicketWithOutcome) domain.GetTicket {
@ -24,25 +22,48 @@ func convertDBTicketOutcomes(ticket dbgen.TicketWithOutcome) domain.GetTicket {
for _, outcome := range ticket.Outcomes { for _, outcome := range ticket.Outcomes {
outcomes = append(outcomes, domain.TicketOutcome{ outcomes = append(outcomes, domain.TicketOutcome{
ID: outcome.ID, ID: outcome.ID,
TicketID: outcome.TicketID, TicketID: outcome.TicketID,
EventID: outcome.EventID, EventID: outcome.EventID,
OddID: outcome.OddID, OddID: outcome.OddID,
HomeTeamName: outcome.HomeTeamName,
AwayTeamName: outcome.AwayTeamName,
MarketID: outcome.MarketID,
MarketName: outcome.MarketName,
Odd: outcome.Odd,
OddName: outcome.OddName,
Expires: outcome.Expires.Time,
}) })
} }
return domain.GetTicket{ return domain.GetTicket{
ID: ticket.ID, ID: ticket.ID,
Amount: domain.Currency(ticket.Amount.Int64), Amount: domain.Currency(ticket.Amount),
TotalOdds: ticket.TotalOdds, TotalOdds: ticket.TotalOdds,
Outcomes: outcomes, Outcomes: outcomes,
} }
} }
func convertDBCreateTicketOutcome(ticketOutcome domain.CreateTicketOutcome) dbgen.CreateTicketOutcomeParams {
return dbgen.CreateTicketOutcomeParams{
TicketID: ticketOutcome.TicketID,
EventID: ticketOutcome.EventID,
OddID: ticketOutcome.OddID,
HomeTeamName: ticketOutcome.HomeTeamName,
AwayTeamName: ticketOutcome.AwayTeamName,
MarketID: ticketOutcome.MarketID,
MarketName: ticketOutcome.MarketName,
Odd: ticketOutcome.Odd,
OddName: ticketOutcome.OddName,
Expires: pgtype.Timestamp{
Time: ticketOutcome.Expires,
Valid: true,
},
}
}
func convertCreateTicket(ticket domain.CreateTicket) dbgen.CreateTicketParams { func convertCreateTicket(ticket domain.CreateTicket) dbgen.CreateTicketParams {
return dbgen.CreateTicketParams{ return dbgen.CreateTicketParams{
Amount: pgtype.Int8{ Amount: int64(ticket.Amount),
Int64: int64(ticket.Amount),
},
TotalOdds: ticket.TotalOdds, TotalOdds: ticket.TotalOdds,
} }
} }
@ -61,11 +82,7 @@ func (s *Store) CreateTicketOutcome(ctx context.Context, outcomes []domain.Creat
var dbParams []dbgen.CreateTicketOutcomeParams = make([]dbgen.CreateTicketOutcomeParams, 0, len(outcomes)) var dbParams []dbgen.CreateTicketOutcomeParams = make([]dbgen.CreateTicketOutcomeParams, 0, len(outcomes))
for _, outcome := range outcomes { for _, outcome := range outcomes {
dbParams = append(dbParams, dbgen.CreateTicketOutcomeParams{ dbParams = append(dbParams, convertDBCreateTicketOutcome(outcome))
TicketID: outcome.TicketID,
EventID: outcome.EventID,
OddID: outcome.OddID,
})
} }
rows, err := s.queries.CreateTicketOutcome(ctx, dbParams) rows, err := s.queries.CreateTicketOutcome(ctx, dbParams)
@ -89,7 +106,6 @@ func (s *Store) GetTicketByID(ctx context.Context, id int64) (domain.GetTicket,
func (s *Store) GetAllTickets(ctx context.Context) ([]domain.GetTicket, error) { func (s *Store) GetAllTickets(ctx context.Context) ([]domain.GetTicket, error) {
tickets, err := s.queries.GetAllTickets(ctx) tickets, err := s.queries.GetAllTickets(ctx)
fmt.Printf("%v", tickets)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -97,7 +113,6 @@ func (s *Store) GetAllTickets(ctx context.Context) ([]domain.GetTicket, error) {
var result []domain.GetTicket = make([]domain.GetTicket, 0, len(tickets)) var result []domain.GetTicket = make([]domain.GetTicket, 0, len(tickets))
for _, ticket := range tickets { for _, ticket := range tickets {
result = append(result, convertDBTicketOutcomes(ticket)) result = append(result, convertDBTicketOutcomes(ticket))
// fmt.Printf("%v", convertDBTicketOutcomes(ticket))
} }
return result, nil return result, nil

View File

@ -9,6 +9,7 @@ import (
type BetStore interface { type BetStore interface {
CreateBet(ctx context.Context, bet domain.CreateBet) (domain.Bet, error) CreateBet(ctx context.Context, bet domain.CreateBet) (domain.Bet, error)
CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBetOutcome) (int64, error) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBetOutcome) (int64, error)
GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error)
GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error)
GetAllBets(ctx context.Context) ([]domain.GetBet, error) GetAllBets(ctx context.Context) ([]domain.GetBet, error)
GetBetByBranchID(ctx context.Context, BranchID int64) ([]domain.GetBet, error) GetBetByBranchID(ctx context.Context, BranchID int64) ([]domain.GetBet, error)

View File

@ -27,6 +27,9 @@ func (s *Service) CreateBetOutcome(ctx context.Context, outcomes []domain.Create
func (s *Service) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) { func (s *Service) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) {
return s.betStore.GetBetByID(ctx, id) return s.betStore.GetBetByID(ctx, id)
} }
func (s *Service) GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error) {
return s.betStore.GetBetByCashoutID(ctx, id)
}
func (s *Service) GetAllBets(ctx context.Context) ([]domain.GetBet, error) { func (s *Service) GetAllBets(ctx context.Context) ([]domain.GetBet, error) {
return s.betStore.GetAllBets(ctx) return s.betStore.GetAllBets(ctx)
} }

View File

@ -64,10 +64,10 @@ func NewApp(
}) })
app.Use(cors.New(cors.Config{ app.Use(cors.New(cors.Config{
AllowOrigins: "http://localhost:8000", // Specify your frontend's origin AllowOrigins: "*", // Specify your frontend's origin
AllowMethods: "GET,POST,PUT,DELETE,OPTIONS", // Specify the allowed HTTP methods AllowMethods: "GET,POST,PUT,DELETE,OPTIONS", // Specify the allowed HTTP methods
AllowHeaders: "Content-Type,Authorization,platform", // Specify the allowed headers AllowHeaders: "Content-Type,Authorization,platform", // Specify the allowed headers
AllowCredentials: true, // AllowCredentials: true,
})) }))
s := &App{ s := &App{

View File

@ -1,61 +1,56 @@
package httpserver package httpserver
import ( import (
// "context" "log"
"log"
eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
"github.com/robfig/cron/v3" "github.com/robfig/cron/v3"
) )
func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.Service) { func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.Service) {
c := cron.New(cron.WithSeconds()) c := cron.New(cron.WithSeconds())
schedule := []struct {
spec string
task func()
}{
// {
// spec: "*/30 * * * * *", // Every 30 seconds
// 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)
// }
// },
// },
schedule := []struct {
spec string
task func()
}{
// { // {
// spec: "*/30 * * * * *", // Every 30 seconds // spec: "*/30 * * * * *", // Every 30 seconds
// task: func() { // task: func() {
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil { // if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
// log.Printf("FetchNonLiveOdds error: %v", err) // log.Printf("FetchUpcomingEvents error: %v", err)
// } // }
// }, // },
// }, // },
}
// {
for _, job := range schedule { // spec: "*/5 * * * * *", // Every 5 seconds
if _, err := c.AddFunc(job.spec, job.task); err != nil { // task: func() {
log.Fatalf("Failed to schedule cron job: %v", err) // if err := eventService.FetchLiveEvents(context.Background()); err != nil {
} // log.Printf("FetchLiveEvents error: %v", err)
} // }
// },
// },
c.Start() // {
log.Println("Cron jobs started for event and odds services") // spec: "*/30 * * * * *", // Every 30 seconds
} // task: func() {
// if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
// log.Printf("FetchNonLiveOdds error: %v", err)
// }
// },
// },
}
for _, job := range schedule {
job.task()
if _, err := c.AddFunc(job.spec, job.task); err != nil {
log.Fatalf("Failed to schedule cron job: %v", err)
}
}
c.Start()
log.Println("Cron jobs started for event and odds services")
}

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"log/slog" "log/slog"
"strconv" "strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
@ -16,10 +17,19 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
type BetOutcome struct { type CreateBetOutcomeReq struct {
EventID int64 `json:"event_id" example:"1"` BetID int64 `json:"bet_id" example:"1"`
OddID int64 `json:"odd_id" example:"1"` EventID int64 `json:"event_id" example:"1"`
OddID int64 `json:"odd_id" example:"1"`
HomeTeamName string `json:"home_team_name" example:"Manchester"`
AwayTeamName string `json:"away_team_name" example:"Liverpool"`
MarketID int64 `json:"market_id" example:"1"`
MarketName string `json:"market_name" example:"Fulltime Result"`
Odd float32 `json:"odd" example:"1.5"`
OddName string `json:"odd_name" example:"1"`
Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
} }
type NullableInt64 struct { type NullableInt64 struct {
Value int64 Value int64
Valid bool Valid bool
@ -49,13 +59,13 @@ func (n NullableInt64) MarshalJSON() ([]byte, error) {
} }
type CreateBetReq struct { type CreateBetReq struct {
Outcomes []BetOutcome `json:"outcomes"` Outcomes []CreateBetOutcomeReq `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"` Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"` TotalOdds float32 `json:"total_odds" example:"4.22"`
Status domain.BetStatus `json:"status" example:"1"` Status domain.BetStatus `json:"status" example:"1"`
FullName string `json:"full_name" example:"John"` FullName string `json:"full_name" example:"John"`
PhoneNumber string `json:"phone_number" example:"1234567890"` PhoneNumber string `json:"phone_number" example:"1234567890"`
IsShopBet bool `json:"is_shop_bet" example:"false"` IsShopBet bool `json:"is_shop_bet" example:"false"`
} }
type CreateBetRes struct { type CreateBetRes struct {
@ -69,6 +79,7 @@ type CreateBetRes struct {
UserID int64 `json:"user_id" example:"2"` UserID int64 `json:"user_id" example:"2"`
IsShopBet bool `json:"is_shop_bet" example:"false"` IsShopBet bool `json:"is_shop_bet" example:"false"`
CreatedNumber int64 `json:"created_number" example:"2"` CreatedNumber int64 `json:"created_number" example:"2"`
CashedID string `json:"cashed_id" example:"21234"`
} }
type BetRes struct { type BetRes struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
@ -81,6 +92,8 @@ type BetRes struct {
BranchID int64 `json:"branch_id" example:"2"` BranchID int64 `json:"branch_id" example:"2"`
UserID int64 `json:"user_id" example:"2"` UserID int64 `json:"user_id" example:"2"`
IsShopBet bool `json:"is_shop_bet" example:"false"` IsShopBet bool `json:"is_shop_bet" example:"false"`
CashedOut bool `json:"cashed_out" example:"false"`
CashedID string `json:"cashed_id" example:"21234"`
} }
func convertCreateBet(bet domain.Bet, createdNumber int64) CreateBetRes { func convertCreateBet(bet domain.Bet, createdNumber int64) CreateBetRes {
@ -94,6 +107,7 @@ func convertCreateBet(bet domain.Bet, createdNumber int64) CreateBetRes {
BranchID: bet.BranchID.Value, BranchID: bet.BranchID.Value,
UserID: bet.UserID.Value, UserID: bet.UserID.Value,
CreatedNumber: createdNumber, CreatedNumber: createdNumber,
CashedID: bet.CashoutID,
} }
} }
@ -107,6 +121,10 @@ func convertBet(bet domain.GetBet) BetRes {
PhoneNumber: bet.PhoneNumber, PhoneNumber: bet.PhoneNumber,
BranchID: bet.BranchID.Value, BranchID: bet.BranchID.Value,
UserID: bet.UserID.Value, UserID: bet.UserID.Value,
Outcomes: bet.Outcomes,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
CashedID: bet.CashoutID,
} }
} }
@ -145,7 +163,7 @@ func CreateBet(logger *slog.Logger, betSvc *bet.Service, userSvc *user.Service,
user, err := userSvc.GetUserByID(c.Context(), userID) user, err := userSvc.GetUserByID(c.Context(), userID)
cashoutUUID := uuid.New() cashoutUUID := uuid.New()
var bet domain.Bet var bet domain.Bet
if user.Role != domain.RoleCashier { if user.Role == domain.RoleCashier {
// Get the branch from the branch ID // Get the branch from the branch ID
branch, err := branchSvc.GetBranchByCashier(c.Context(), user.ID) branch, err := branchSvc.GetBranchByCashier(c.Context(), user.ID)
@ -167,6 +185,7 @@ func CreateBet(logger *slog.Logger, betSvc *bet.Service, userSvc *user.Service,
"error": "Unable to deduct from branch wallet", "error": "Unable to deduct from branch wallet",
}) })
} }
bet, err = betSvc.CreateBet(c.Context(), domain.CreateBet{ bet, err = betSvc.CreateBet(c.Context(), domain.CreateBet{
Amount: domain.ToCurrency(req.Amount), Amount: domain.ToCurrency(req.Amount),
TotalOdds: req.TotalOdds, TotalOdds: req.TotalOdds,
@ -217,9 +236,16 @@ func CreateBet(logger *slog.Logger, betSvc *bet.Service, userSvc *user.Service,
for _, outcome := range req.Outcomes { for _, outcome := range req.Outcomes {
outcomes = append(outcomes, domain.CreateBetOutcome{ outcomes = append(outcomes, domain.CreateBetOutcome{
BetID: bet.ID, BetID: bet.ID,
EventID: outcome.EventID, EventID: outcome.EventID,
OddID: outcome.OddID, OddID: outcome.OddID,
HomeTeamName: outcome.HomeTeamName,
AwayTeamName: outcome.AwayTeamName,
MarketID: outcome.MarketID,
MarketName: outcome.MarketName,
Odd: outcome.Odd,
OddName: outcome.OddName,
Expires: outcome.Expires,
}) })
} }
rows, err := betSvc.CreateBetOutcome(c.Context(), outcomes) rows, err := betSvc.CreateBetOutcome(c.Context(), outcomes)
@ -287,7 +313,6 @@ func GetBetByID(logger *slog.Logger, betSvc *bet.Service, validator *customvalid
} }
bet, err := betSvc.GetBetByID(c.Context(), id) bet, err := betSvc.GetBetByID(c.Context(), id)
if err != nil { if err != nil {
logger.Error("Failed to get bet by ID", "betID", id, "error", err) logger.Error("Failed to get bet by ID", "betID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bet", err, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bet", err, nil)
@ -300,6 +325,40 @@ func GetBetByID(logger *slog.Logger, betSvc *bet.Service, validator *customvalid
} }
} }
// GetBetByCashoutID godoc
// @Summary Gets bet by cashout id
// @Description Gets a single bet by cashout id
// @Tags bet
// @Accept json
// @Produce json
// @Param id path string true "cashout ID"
// @Success 200 {object} BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /bet/cashout/{id} [get]
func GetBetByCashoutID(logger *slog.Logger, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
cashoutID := c.Params("id")
// id, err := strconv.ParseInt(cashoutID, 10, 64)
// if err != nil {
// logger.Error("Invalid cashout ID", "cashoutID", cashoutID, "error", err)
// return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid cashout ID", err, nil)
// }
bet, err := betSvc.GetBetByCashoutID(c.Context(), cashoutID)
if err != nil {
logger.Error("Failed to get bet by ID", "cashoutID", cashoutID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bet", err, nil)
}
res := convertBet(bet)
return response.WriteJSON(c, fiber.StatusOK, "Bet retrieved successfully", res, nil)
}
}
type UpdateCashOutReq struct { type UpdateCashOutReq struct {
CashedOut bool CashedOut bool
} }

View File

@ -3,6 +3,7 @@ package handlers
import ( import (
"log/slog" "log/slog"
"strconv" "strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
@ -11,15 +12,23 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type TicketOutcome struct { type CreateTicketOutcomeReq struct {
EventID int64 `json:"event_id" example:"1"` TicketID int64 `json:"ticket_id" example:"1"`
OddID int64 `json:"odd_id" example:"1"` EventID int64 `json:"event_id" example:"1"`
OddID int64 `json:"odd_id" example:"1"`
HomeTeamName string `json:"home_team_name" example:"Manchester"`
AwayTeamName string `json:"away_team_name" example:"Liverpool"`
MarketID int64 `json:"market_id" example:"1"`
MarketName string `json:"market_name" example:"Fulltime Result"`
Odd float32 `json:"odd" example:"1.5"`
OddName string `json:"odd_name" example:"1"`
Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"`
} }
type CreateTicketReq struct { type CreateTicketReq struct {
Outcomes []TicketOutcome `json:"outcomes"` Outcomes []CreateTicketOutcomeReq `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"` Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"` TotalOdds float32 `json:"total_odds" example:"4.22"`
} }
type CreateTicketRes struct { type CreateTicketRes struct {
FastCode int64 `json:"fast_code" example:"1234"` FastCode int64 `json:"fast_code" example:"1234"`
@ -71,9 +80,16 @@ func CreateTicket(logger *slog.Logger, ticketSvc *ticket.Service,
for _, outcome := range req.Outcomes { for _, outcome := range req.Outcomes {
outcomes = append(outcomes, domain.CreateTicketOutcome{ outcomes = append(outcomes, domain.CreateTicketOutcome{
TicketID: ticket.ID, TicketID: ticket.ID,
EventID: outcome.EventID, EventID: outcome.EventID,
OddID: outcome.OddID, OddID: outcome.OddID,
HomeTeamName: outcome.HomeTeamName,
AwayTeamName: outcome.AwayTeamName,
MarketID: outcome.MarketID,
MarketName: outcome.MarketName,
Odd: outcome.Odd,
OddName: outcome.OddName,
Expires: outcome.Expires,
}) })
} }
rows, err := ticketSvc.CreateTicketOutcome(c.Context(), outcomes) rows, err := ticketSvc.CreateTicketOutcome(c.Context(), outcomes)

View File

@ -6,6 +6,8 @@ import (
"strconv" "strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
@ -32,20 +34,18 @@ type TransactionRes struct {
} }
type CreateTransactionReq struct { type CreateTransactionReq struct {
Amount float32 `json:"amount" example:"100.0"` CashoutID string `json:"cashout_id" example:"191212"`
BranchID int64 `json:"branch_id" example:"1"` Amount float32 `json:"amount" example:"100.0"`
CashierID int64 `json:"cashier_id" example:"1"` BetID int64 `json:"bet_id" example:"1"`
BetID int64 `json:"bet_id" example:"1"` Type int64 `json:"type" example:"1"`
Type int64 `json:"type" example:"1"` PaymentOption domain.PaymentOption `json:"payment_option" example:"1"`
PaymentOption domain.PaymentOption `json:"payment_option" example:"1"` FullName string `json:"full_name" example:"John Smith"`
FullName string `json:"full_name" example:"John Smith"` PhoneNumber string `json:"phone_number" example:"0911111111"`
PhoneNumber string `json:"phone_number" example:"0911111111"` BankCode string `json:"bank_code"`
// Payment Details for bank BeneficiaryName string `json:"beneficiary_name"`
BankCode string `json:"bank_code"` AccountName string `json:"account_name"`
BeneficiaryName string `json:"beneficiary_name"` AccountNumber string `json:"account_number"`
AccountName string `json:"account_name"` ReferenceNumber string `json:"reference_number"`
AccountNumber string `json:"account_number"`
ReferenceNumber string `json:"reference_number"`
} }
func convertTransaction(transaction domain.Transaction) TransactionRes { func convertTransaction(transaction domain.Transaction) TransactionRes {
@ -79,8 +79,42 @@ func convertTransaction(transaction domain.Transaction) TransactionRes {
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /transaction [post] // @Router /transaction [post]
func CreateTransaction(logger *slog.Logger, transactionSvc *transaction.Service, validator *customvalidator.CustomValidator) fiber.Handler { func CreateTransaction(logger *slog.Logger, transactionSvc *transaction.Service, userSvc *user.Service, branchSvc *branch.Service, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
userID := c.Locals("user_id").(int64)
user, err := userSvc.GetUserByID(c.Context(), userID)
if user.Role == domain.RoleCustomer {
logger.Error("CreateTransactionReq failed")
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "unauthorized access",
})
}
// TODO: Add validation to make sure that the bet hasn't already been cashed out by someone else
var branchID int64
if user.Role == domain.RoleAdmin || user.Role == domain.RoleBranchManager || user.Role == domain.RoleSuperAdmin {
branch, err := branchSvc.GetBranchByID(c.Context(), 1)
if err != nil {
logger.Error("CreateTransactionReq no branches")
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "This user type doesn't have branches",
})
}
branchID = branch.ID
} else {
branch, err := branchSvc.GetBranchByCashier(c.Context(), user.ID)
if err != nil {
logger.Error("CreateTransactionReq failed, branch id invalid")
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Branch ID invalid",
})
}
branchID = branch.ID
}
var req CreateTransactionReq var req CreateTransactionReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
logger.Error("CreateTransactionReq failed", "error", err) logger.Error("CreateTransactionReq failed", "error", err)
@ -91,14 +125,15 @@ func CreateTransaction(logger *slog.Logger, transactionSvc *transaction.Service,
valErrs, ok := validator.Validate(c, req) valErrs, ok := validator.Validate(c, req)
if !ok { if !ok {
logger.Error("CreateTransactionReq failed v", "error", valErrs)
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil return nil
} }
transaction, err := transactionSvc.CreateTransaction(c.Context(), domain.CreateTransaction{ transaction, err := transactionSvc.CreateTransaction(c.Context(), domain.CreateTransaction{
BranchID: branchID,
CashierID: userID,
Amount: domain.ToCurrency(req.Amount), Amount: domain.ToCurrency(req.Amount),
BranchID: req.BranchID,
CashierID: req.CashierID,
BetID: req.BetID, BetID: req.BetID,
Type: domain.TransactionType(req.Type), Type: domain.TransactionType(req.Type),
PaymentOption: domain.PaymentOption(req.PaymentOption), PaymentOption: domain.PaymentOption(req.PaymentOption),
@ -116,6 +151,13 @@ func CreateTransaction(logger *slog.Logger, transactionSvc *transaction.Service,
return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
} }
err = betSvc.UpdateCashOut(c.Context(), req.BetID, true)
if err != nil {
logger.Error("CreateTransactionReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
}
res := convertTransaction(transaction) res := convertTransaction(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction Created", res, nil) return response.WriteJSON(c, fiber.StatusOK, "Transaction Created", res, nil)
@ -175,8 +217,8 @@ func GetAllTransactions(
// Convert transactions to response format // Convert transactions to response format
var res []TransactionRes = make([]TransactionRes, 0, len(transactions)) var res []TransactionRes = make([]TransactionRes, 0, len(transactions))
for i, transaction := range transactions { for _, transaction := range transactions {
res[i] = convertTransaction(transaction) res = append(res, convertTransaction(transaction))
} }
return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil) return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil)

View File

@ -91,9 +91,10 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/ticket/:id", handlers.GetTicketByID(a.logger, a.ticketSvc, a.validator)) a.fiber.Get("/ticket/:id", handlers.GetTicketByID(a.logger, a.ticketSvc, a.validator))
// Bet // Bet
a.fiber.Post("/bet", handlers.CreateBet(a.logger, a.betSvc, a.userSvc, a.branchSvc, a.walletSvc, a.validator)) a.fiber.Post("/bet", a.authMiddleware, handlers.CreateBet(a.logger, a.betSvc, a.userSvc, a.branchSvc, a.walletSvc, a.validator))
a.fiber.Get("/bet", handlers.GetAllBet(a.logger, a.betSvc, a.validator)) a.fiber.Get("/bet", handlers.GetAllBet(a.logger, a.betSvc, a.validator))
a.fiber.Get("/bet/:id", handlers.GetBetByID(a.logger, a.betSvc, a.validator)) a.fiber.Get("/bet/:id", handlers.GetBetByID(a.logger, a.betSvc, a.validator))
a.fiber.Get("/bet/cashout/:id", handlers.GetBetByCashoutID(a.logger, a.betSvc, a.validator))
a.fiber.Patch("/bet/:id", handlers.UpdateCashOut(a.logger, a.betSvc, a.validator)) a.fiber.Patch("/bet/:id", handlers.UpdateCashOut(a.logger, a.betSvc, a.validator))
a.fiber.Delete("/bet/:id", handlers.DeleteBet(a.logger, a.betSvc, a.validator)) a.fiber.Delete("/bet/:id", handlers.DeleteBet(a.logger, a.betSvc, a.validator))
@ -110,7 +111,7 @@ func (a *App) initAppRoutes() {
a.fiber.Post("/transfer/refill/:id", a.authMiddleware, handlers.RefillWallet(a.logger, a.walletSvc, a.validator)) a.fiber.Post("/transfer/refill/:id", a.authMiddleware, handlers.RefillWallet(a.logger, a.walletSvc, a.validator))
// Transactions // Transactions
a.fiber.Post("/transaction", a.authMiddleware, handlers.CreateTransaction(a.logger, a.transactionSvc, a.validator)) a.fiber.Post("/transaction", a.authMiddleware, handlers.CreateTransaction(a.logger, a.transactionSvc, a.userSvc, a.branchSvc, a.betSvc, a.validator))
a.fiber.Get("/transaction", a.authMiddleware, handlers.GetAllTransactions(a.logger, a.transactionSvc, a.userSvc, a.validator)) a.fiber.Get("/transaction", a.authMiddleware, handlers.GetAllTransactions(a.logger, a.transactionSvc, a.userSvc, a.validator))
a.fiber.Get("/transaction/:id", a.authMiddleware, handlers.GetTransactionByID(a.logger, a.transactionSvc, a.validator)) a.fiber.Get("/transaction/:id", a.authMiddleware, handlers.GetTransactionByID(a.logger, a.transactionSvc, a.validator))
a.fiber.Patch("/transaction/:id", a.authMiddleware, handlers.UpdateTransactionVerified(a.logger, a.transactionSvc, a.validator)) a.fiber.Patch("/transaction/:id", a.authMiddleware, handlers.UpdateTransactionVerified(a.logger, a.transactionSvc, a.validator))