Merge remote-tracking branch 'refs/remotes/origin/auth' into auth
This commit is contained in:
commit
0879527c9d
|
|
@ -82,7 +82,7 @@ func main() {
|
||||||
app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{
|
app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{
|
||||||
JwtAccessKey: cfg.JwtKey,
|
JwtAccessKey: cfg.JwtKey,
|
||||||
JwtAccessExpiry: cfg.AccessExpiry,
|
JwtAccessExpiry: cfg.AccessExpiry,
|
||||||
}, userSvc, ticketSvc, betSvc, walletSvc, transactionSvc, branchSvc, notificationSvc, oddsSvc)
|
}, userSvc, ticketSvc, betSvc, walletSvc, transactionSvc, branchSvc, notificationSvc, oddsSvc, eventSvc)
|
||||||
|
|
||||||
logger.Info("Starting server", "port", cfg.Port)
|
logger.Info("Starting server", "port", cfg.Port)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,81 @@ ON CONFLICT (id) DO UPDATE SET
|
||||||
is_live = EXCLUDED.is_live,
|
is_live = EXCLUDED.is_live,
|
||||||
status = EXCLUDED.status,
|
status = EXCLUDED.status,
|
||||||
fetched_at = now();
|
fetched_at = now();
|
||||||
|
-- name: InsertUpcomingEvent :exec
|
||||||
|
INSERT INTO events (
|
||||||
|
id, sport_id, match_name, home_team, away_team,
|
||||||
|
home_team_id, away_team_id, home_kit_image, away_kit_image,
|
||||||
|
league_id, league_name, league_cc, start_time,
|
||||||
|
is_live, status
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5,
|
||||||
|
$6, $7, $8, $9,
|
||||||
|
$10, $11, $12, $13,
|
||||||
|
false, 'upcoming'
|
||||||
|
)
|
||||||
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
|
sport_id = EXCLUDED.sport_id,
|
||||||
|
match_name = EXCLUDED.match_name,
|
||||||
|
home_team = EXCLUDED.home_team,
|
||||||
|
away_team = EXCLUDED.away_team,
|
||||||
|
home_team_id = EXCLUDED.home_team_id,
|
||||||
|
away_team_id = EXCLUDED.away_team_id,
|
||||||
|
home_kit_image = EXCLUDED.home_kit_image,
|
||||||
|
away_kit_image = EXCLUDED.away_kit_image,
|
||||||
|
league_id = EXCLUDED.league_id,
|
||||||
|
league_name = EXCLUDED.league_name,
|
||||||
|
league_cc = EXCLUDED.league_cc,
|
||||||
|
start_time = EXCLUDED.start_time,
|
||||||
|
is_live = false,
|
||||||
|
status = 'upcoming',
|
||||||
|
fetched_at = now();
|
||||||
|
|
||||||
|
|
||||||
-- name: ListLiveEvents :many
|
-- name: ListLiveEvents :many
|
||||||
SELECT id FROM events WHERE is_live = true;
|
SELECT id FROM events WHERE is_live = true;
|
||||||
|
|
||||||
|
-- name: GetAllUpcomingEvents :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
sport_id,
|
||||||
|
match_name,
|
||||||
|
home_team,
|
||||||
|
away_team,
|
||||||
|
home_team_id,
|
||||||
|
away_team_id,
|
||||||
|
home_kit_image,
|
||||||
|
away_kit_image,
|
||||||
|
league_id,
|
||||||
|
league_name,
|
||||||
|
league_cc,
|
||||||
|
start_time,
|
||||||
|
is_live,
|
||||||
|
status,
|
||||||
|
fetched_at
|
||||||
|
FROM events
|
||||||
|
WHERE is_live = false
|
||||||
|
AND status = 'upcoming'
|
||||||
|
ORDER BY start_time ASC;
|
||||||
|
-- name: GetUpcomingByID :one
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
sport_id,
|
||||||
|
match_name,
|
||||||
|
home_team,
|
||||||
|
away_team,
|
||||||
|
home_team_id,
|
||||||
|
away_team_id,
|
||||||
|
home_kit_image,
|
||||||
|
away_kit_image,
|
||||||
|
league_id,
|
||||||
|
league_name,
|
||||||
|
league_cc,
|
||||||
|
start_time,
|
||||||
|
is_live,
|
||||||
|
status,
|
||||||
|
fetched_at
|
||||||
|
FROM events
|
||||||
|
WHERE id = $1
|
||||||
|
AND is_live = false
|
||||||
|
AND status = 'upcoming'
|
||||||
|
LIMIT 1;
|
||||||
|
|
|
||||||
151
docs/docs.go
151
docs/docs.go
|
|
@ -1246,6 +1246,82 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/prematch/events": {
|
||||||
|
"get": {
|
||||||
|
"description": "Retrieve all upcoming events from the database",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"prematch"
|
||||||
|
],
|
||||||
|
"summary": "Retrieve all upcoming events",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.UpcomingEvent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/prematch/events/{id}": {
|
||||||
|
"get": {
|
||||||
|
"description": "Retrieve an upcoming event by ID",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"prematch"
|
||||||
|
],
|
||||||
|
"summary": "Retrieve an upcoming by ID",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.UpcomingEvent"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/prematch/odds": {
|
"/prematch/odds": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Retrieve all prematch odds from the database",
|
"description": "Retrieve all prematch odds from the database",
|
||||||
|
|
@ -2580,17 +2656,78 @@ const docTemplate = `{
|
||||||
"domain.TicketOutcome": {
|
"domain.TicketOutcome": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"eventID": {
|
"event_id": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
},
|
},
|
||||||
"oddID": {
|
"odd_id": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
},
|
},
|
||||||
"ticketID": {
|
"ticket_id": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.UpcomingEvent": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"awayKitImage": {
|
||||||
|
"description": "Kit or image for away team (optional)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"awayTeam": {
|
||||||
|
"description": "Away team name (can be empty/null)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"awayTeamID": {
|
||||||
|
"description": "Away team ID (can be empty/null)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"homeKitImage": {
|
||||||
|
"description": "Kit or image for home team (optional)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"homeTeam": {
|
||||||
|
"description": "Home team name (if available)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"homeTeamID": {
|
||||||
|
"description": "Home team ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"description": "Event ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"leagueCC": {
|
||||||
|
"description": "League country code",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"leagueID": {
|
||||||
|
"description": "League ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"leagueName": {
|
||||||
|
"description": "League name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"matchName": {
|
||||||
|
"description": "Match or event name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sportID": {
|
||||||
|
"description": "Sport ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"startTime": {
|
||||||
|
"description": "Converted from \"time\" field in UNIX format",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1238,6 +1238,82 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/prematch/events": {
|
||||||
|
"get": {
|
||||||
|
"description": "Retrieve all upcoming events from the database",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"prematch"
|
||||||
|
],
|
||||||
|
"summary": "Retrieve all upcoming events",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.UpcomingEvent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/prematch/events/{id}": {
|
||||||
|
"get": {
|
||||||
|
"description": "Retrieve an upcoming event by ID",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"prematch"
|
||||||
|
],
|
||||||
|
"summary": "Retrieve an upcoming by ID",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.UpcomingEvent"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/prematch/odds": {
|
"/prematch/odds": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Retrieve all prematch odds from the database",
|
"description": "Retrieve all prematch odds from the database",
|
||||||
|
|
@ -2572,17 +2648,78 @@
|
||||||
"domain.TicketOutcome": {
|
"domain.TicketOutcome": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"eventID": {
|
"event_id": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
},
|
},
|
||||||
"oddID": {
|
"odd_id": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
},
|
},
|
||||||
"ticketID": {
|
"ticket_id": {
|
||||||
"type": "integer"
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.UpcomingEvent": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"awayKitImage": {
|
||||||
|
"description": "Kit or image for away team (optional)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"awayTeam": {
|
||||||
|
"description": "Away team name (can be empty/null)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"awayTeamID": {
|
||||||
|
"description": "Away team ID (can be empty/null)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"homeKitImage": {
|
||||||
|
"description": "Kit or image for home team (optional)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"homeTeam": {
|
||||||
|
"description": "Home team name (if available)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"homeTeamID": {
|
||||||
|
"description": "Home team ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"description": "Event ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"leagueCC": {
|
||||||
|
"description": "League country code",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"leagueID": {
|
||||||
|
"description": "League ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"leagueName": {
|
||||||
|
"description": "League name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"matchName": {
|
||||||
|
"description": "Match or event name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sportID": {
|
||||||
|
"description": "Sport ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"startTime": {
|
||||||
|
"description": "Converted from \"time\" field in UNIX format",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -100,15 +100,61 @@ definitions:
|
||||||
- RoleCashier
|
- RoleCashier
|
||||||
domain.TicketOutcome:
|
domain.TicketOutcome:
|
||||||
properties:
|
properties:
|
||||||
eventID:
|
event_id:
|
||||||
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
id:
|
id:
|
||||||
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
oddID:
|
odd_id:
|
||||||
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
ticketID:
|
ticket_id:
|
||||||
|
example: 1
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
domain.UpcomingEvent:
|
||||||
|
properties:
|
||||||
|
awayKitImage:
|
||||||
|
description: Kit or image for away team (optional)
|
||||||
|
type: string
|
||||||
|
awayTeam:
|
||||||
|
description: Away team name (can be empty/null)
|
||||||
|
type: string
|
||||||
|
awayTeamID:
|
||||||
|
description: Away team ID (can be empty/null)
|
||||||
|
type: string
|
||||||
|
homeKitImage:
|
||||||
|
description: Kit or image for home team (optional)
|
||||||
|
type: string
|
||||||
|
homeTeam:
|
||||||
|
description: Home team name (if available)
|
||||||
|
type: string
|
||||||
|
homeTeamID:
|
||||||
|
description: Home team ID
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
description: Event ID
|
||||||
|
type: string
|
||||||
|
leagueCC:
|
||||||
|
description: League country code
|
||||||
|
type: string
|
||||||
|
leagueID:
|
||||||
|
description: League ID
|
||||||
|
type: string
|
||||||
|
leagueName:
|
||||||
|
description: League name
|
||||||
|
type: string
|
||||||
|
matchName:
|
||||||
|
description: Match or event name
|
||||||
|
type: string
|
||||||
|
sportID:
|
||||||
|
description: Sport ID
|
||||||
|
type: string
|
||||||
|
startTime:
|
||||||
|
description: Converted from "time" field in UNIX format
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
handlers.BetOutcome:
|
handlers.BetOutcome:
|
||||||
properties:
|
properties:
|
||||||
event_id:
|
event_id:
|
||||||
|
|
@ -1562,6 +1608,56 @@ paths:
|
||||||
summary: Create a operation
|
summary: Create a operation
|
||||||
tags:
|
tags:
|
||||||
- branch
|
- branch
|
||||||
|
/prematch/events:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Retrieve all upcoming events from the database
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.UpcomingEvent'
|
||||||
|
type: array
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Retrieve all upcoming events
|
||||||
|
tags:
|
||||||
|
- prematch
|
||||||
|
/prematch/events/{id}:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Retrieve an upcoming event by ID
|
||||||
|
parameters:
|
||||||
|
- description: ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.UpcomingEvent'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.APIResponse'
|
||||||
|
summary: Retrieve an upcoming by ID
|
||||||
|
tags:
|
||||||
|
- prematch
|
||||||
/prematch/odds:
|
/prematch/odds:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,154 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const GetAllUpcomingEvents = `-- name: GetAllUpcomingEvents :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
sport_id,
|
||||||
|
match_name,
|
||||||
|
home_team,
|
||||||
|
away_team,
|
||||||
|
home_team_id,
|
||||||
|
away_team_id,
|
||||||
|
home_kit_image,
|
||||||
|
away_kit_image,
|
||||||
|
league_id,
|
||||||
|
league_name,
|
||||||
|
league_cc,
|
||||||
|
start_time,
|
||||||
|
is_live,
|
||||||
|
status,
|
||||||
|
fetched_at
|
||||||
|
FROM events
|
||||||
|
WHERE is_live = false
|
||||||
|
AND status = 'upcoming'
|
||||||
|
ORDER BY start_time ASC
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetAllUpcomingEventsRow struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
SportID pgtype.Text `json:"sport_id"`
|
||||||
|
MatchName pgtype.Text `json:"match_name"`
|
||||||
|
HomeTeam pgtype.Text `json:"home_team"`
|
||||||
|
AwayTeam pgtype.Text `json:"away_team"`
|
||||||
|
HomeTeamID pgtype.Text `json:"home_team_id"`
|
||||||
|
AwayTeamID pgtype.Text `json:"away_team_id"`
|
||||||
|
HomeKitImage pgtype.Text `json:"home_kit_image"`
|
||||||
|
AwayKitImage pgtype.Text `json:"away_kit_image"`
|
||||||
|
LeagueID pgtype.Text `json:"league_id"`
|
||||||
|
LeagueName pgtype.Text `json:"league_name"`
|
||||||
|
LeagueCc pgtype.Text `json:"league_cc"`
|
||||||
|
StartTime pgtype.Timestamp `json:"start_time"`
|
||||||
|
IsLive pgtype.Bool `json:"is_live"`
|
||||||
|
Status pgtype.Text `json:"status"`
|
||||||
|
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetAllUpcomingEvents(ctx context.Context) ([]GetAllUpcomingEventsRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetAllUpcomingEvents)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetAllUpcomingEventsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetAllUpcomingEventsRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.SportID,
|
||||||
|
&i.MatchName,
|
||||||
|
&i.HomeTeam,
|
||||||
|
&i.AwayTeam,
|
||||||
|
&i.HomeTeamID,
|
||||||
|
&i.AwayTeamID,
|
||||||
|
&i.HomeKitImage,
|
||||||
|
&i.AwayKitImage,
|
||||||
|
&i.LeagueID,
|
||||||
|
&i.LeagueName,
|
||||||
|
&i.LeagueCc,
|
||||||
|
&i.StartTime,
|
||||||
|
&i.IsLive,
|
||||||
|
&i.Status,
|
||||||
|
&i.FetchedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetUpcomingByID = `-- name: GetUpcomingByID :one
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
sport_id,
|
||||||
|
match_name,
|
||||||
|
home_team,
|
||||||
|
away_team,
|
||||||
|
home_team_id,
|
||||||
|
away_team_id,
|
||||||
|
home_kit_image,
|
||||||
|
away_kit_image,
|
||||||
|
league_id,
|
||||||
|
league_name,
|
||||||
|
league_cc,
|
||||||
|
start_time,
|
||||||
|
is_live,
|
||||||
|
status,
|
||||||
|
fetched_at
|
||||||
|
FROM events
|
||||||
|
WHERE id = $1
|
||||||
|
AND is_live = false
|
||||||
|
AND status = 'upcoming'
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetUpcomingByIDRow struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
SportID pgtype.Text `json:"sport_id"`
|
||||||
|
MatchName pgtype.Text `json:"match_name"`
|
||||||
|
HomeTeam pgtype.Text `json:"home_team"`
|
||||||
|
AwayTeam pgtype.Text `json:"away_team"`
|
||||||
|
HomeTeamID pgtype.Text `json:"home_team_id"`
|
||||||
|
AwayTeamID pgtype.Text `json:"away_team_id"`
|
||||||
|
HomeKitImage pgtype.Text `json:"home_kit_image"`
|
||||||
|
AwayKitImage pgtype.Text `json:"away_kit_image"`
|
||||||
|
LeagueID pgtype.Text `json:"league_id"`
|
||||||
|
LeagueName pgtype.Text `json:"league_name"`
|
||||||
|
LeagueCc pgtype.Text `json:"league_cc"`
|
||||||
|
StartTime pgtype.Timestamp `json:"start_time"`
|
||||||
|
IsLive pgtype.Bool `json:"is_live"`
|
||||||
|
Status pgtype.Text `json:"status"`
|
||||||
|
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetUpcomingByID(ctx context.Context, id string) (GetUpcomingByIDRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetUpcomingByID, id)
|
||||||
|
var i GetUpcomingByIDRow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.SportID,
|
||||||
|
&i.MatchName,
|
||||||
|
&i.HomeTeam,
|
||||||
|
&i.AwayTeam,
|
||||||
|
&i.HomeTeamID,
|
||||||
|
&i.AwayTeamID,
|
||||||
|
&i.HomeKitImage,
|
||||||
|
&i.AwayKitImage,
|
||||||
|
&i.LeagueID,
|
||||||
|
&i.LeagueName,
|
||||||
|
&i.LeagueCc,
|
||||||
|
&i.StartTime,
|
||||||
|
&i.IsLive,
|
||||||
|
&i.Status,
|
||||||
|
&i.FetchedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const InsertEvent = `-- name: InsertEvent :exec
|
const InsertEvent = `-- name: InsertEvent :exec
|
||||||
INSERT INTO events (
|
INSERT INTO events (
|
||||||
id, sport_id, match_name, home_team, away_team,
|
id, sport_id, match_name, home_team, away_team,
|
||||||
|
|
@ -97,6 +245,71 @@ func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const InsertUpcomingEvent = `-- name: InsertUpcomingEvent :exec
|
||||||
|
INSERT INTO events (
|
||||||
|
id, sport_id, match_name, home_team, away_team,
|
||||||
|
home_team_id, away_team_id, home_kit_image, away_kit_image,
|
||||||
|
league_id, league_name, league_cc, start_time,
|
||||||
|
is_live, status
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5,
|
||||||
|
$6, $7, $8, $9,
|
||||||
|
$10, $11, $12, $13,
|
||||||
|
false, 'upcoming'
|
||||||
|
)
|
||||||
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
|
sport_id = EXCLUDED.sport_id,
|
||||||
|
match_name = EXCLUDED.match_name,
|
||||||
|
home_team = EXCLUDED.home_team,
|
||||||
|
away_team = EXCLUDED.away_team,
|
||||||
|
home_team_id = EXCLUDED.home_team_id,
|
||||||
|
away_team_id = EXCLUDED.away_team_id,
|
||||||
|
home_kit_image = EXCLUDED.home_kit_image,
|
||||||
|
away_kit_image = EXCLUDED.away_kit_image,
|
||||||
|
league_id = EXCLUDED.league_id,
|
||||||
|
league_name = EXCLUDED.league_name,
|
||||||
|
league_cc = EXCLUDED.league_cc,
|
||||||
|
start_time = EXCLUDED.start_time,
|
||||||
|
is_live = false,
|
||||||
|
status = 'upcoming',
|
||||||
|
fetched_at = now()
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertUpcomingEventParams struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
SportID pgtype.Text `json:"sport_id"`
|
||||||
|
MatchName pgtype.Text `json:"match_name"`
|
||||||
|
HomeTeam pgtype.Text `json:"home_team"`
|
||||||
|
AwayTeam pgtype.Text `json:"away_team"`
|
||||||
|
HomeTeamID pgtype.Text `json:"home_team_id"`
|
||||||
|
AwayTeamID pgtype.Text `json:"away_team_id"`
|
||||||
|
HomeKitImage pgtype.Text `json:"home_kit_image"`
|
||||||
|
AwayKitImage pgtype.Text `json:"away_kit_image"`
|
||||||
|
LeagueID pgtype.Text `json:"league_id"`
|
||||||
|
LeagueName pgtype.Text `json:"league_name"`
|
||||||
|
LeagueCc pgtype.Text `json:"league_cc"`
|
||||||
|
StartTime pgtype.Timestamp `json:"start_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertUpcomingEvent(ctx context.Context, arg InsertUpcomingEventParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, InsertUpcomingEvent,
|
||||||
|
arg.ID,
|
||||||
|
arg.SportID,
|
||||||
|
arg.MatchName,
|
||||||
|
arg.HomeTeam,
|
||||||
|
arg.AwayTeam,
|
||||||
|
arg.HomeTeamID,
|
||||||
|
arg.AwayTeamID,
|
||||||
|
arg.HomeKitImage,
|
||||||
|
arg.AwayKitImage,
|
||||||
|
arg.LeagueID,
|
||||||
|
arg.LeagueName,
|
||||||
|
arg.LeagueCc,
|
||||||
|
arg.StartTime,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const ListLiveEvents = `-- name: ListLiveEvents :many
|
const ListLiveEvents = `-- name: ListLiveEvents :many
|
||||||
SELECT id FROM events WHERE is_live = true
|
SELECT id FROM events WHERE is_live = true
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -34,22 +34,22 @@ WHERE is_active = true AND source = 'b365api'
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetALLPrematchOddsRow struct {
|
type GetALLPrematchOddsRow struct {
|
||||||
ID int32
|
ID int32 `json:"id"`
|
||||||
EventID pgtype.Text
|
EventID pgtype.Text `json:"event_id"`
|
||||||
Fi pgtype.Text
|
Fi pgtype.Text `json:"fi"`
|
||||||
MarketType string
|
MarketType string `json:"market_type"`
|
||||||
MarketName pgtype.Text
|
MarketName pgtype.Text `json:"market_name"`
|
||||||
MarketCategory pgtype.Text
|
MarketCategory pgtype.Text `json:"market_category"`
|
||||||
MarketID pgtype.Text
|
MarketID pgtype.Text `json:"market_id"`
|
||||||
Name pgtype.Text
|
Name pgtype.Text `json:"name"`
|
||||||
Handicap pgtype.Text
|
Handicap pgtype.Text `json:"handicap"`
|
||||||
OddsValue pgtype.Float8
|
OddsValue pgtype.Float8 `json:"odds_value"`
|
||||||
Section string
|
Section string `json:"section"`
|
||||||
Category pgtype.Text
|
Category pgtype.Text `json:"category"`
|
||||||
RawOdds []byte
|
RawOdds []byte `json:"raw_odds"`
|
||||||
FetchedAt pgtype.Timestamp
|
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
||||||
Source pgtype.Text
|
Source pgtype.Text `json:"source"`
|
||||||
IsActive pgtype.Bool
|
IsActive pgtype.Bool `json:"is_active"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]GetALLPrematchOddsRow, error) {
|
func (q *Queries) GetALLPrematchOdds(ctx context.Context) ([]GetALLPrematchOddsRow, error) {
|
||||||
|
|
@ -167,10 +167,10 @@ LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetRawOddsByIDRow struct {
|
type GetRawOddsByIDRow struct {
|
||||||
ID int32
|
ID int32 `json:"id"`
|
||||||
EventID pgtype.Text
|
EventID pgtype.Text `json:"event_id"`
|
||||||
RawOdds []byte
|
RawOdds []byte `json:"raw_odds"`
|
||||||
FetchedAt pgtype.Timestamp
|
FetchedAt pgtype.Timestamp `json:"fetched_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetRawOddsByID(ctx context.Context, dollar_1 []byte) (GetRawOddsByIDRow, error) {
|
func (q *Queries) GetRawOddsByID(ctx context.Context, dollar_1 []byte) (GetRawOddsByIDRow, error) {
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,41 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
ID string
|
ID string
|
||||||
SportID string
|
SportID string
|
||||||
MatchName string
|
MatchName string
|
||||||
HomeTeam string
|
HomeTeam string
|
||||||
AwayTeam string
|
AwayTeam string
|
||||||
HomeTeamID string
|
HomeTeamID string
|
||||||
AwayTeamID string
|
AwayTeamID string
|
||||||
HomeKitImage string
|
HomeKitImage string
|
||||||
AwayKitImage string
|
AwayKitImage string
|
||||||
LeagueID string
|
LeagueID string
|
||||||
LeagueName string
|
LeagueName string
|
||||||
LeagueCC string
|
LeagueCC string
|
||||||
StartTime string
|
StartTime string
|
||||||
Score string
|
Score string
|
||||||
MatchMinute int
|
MatchMinute int
|
||||||
TimerStatus string
|
TimerStatus string
|
||||||
AddedTime int
|
AddedTime int
|
||||||
MatchPeriod int
|
MatchPeriod int
|
||||||
IsLive bool
|
IsLive bool
|
||||||
Status string
|
Status string
|
||||||
|
}
|
||||||
|
type UpcomingEvent struct {
|
||||||
|
ID string // Event ID
|
||||||
|
SportID string // Sport ID
|
||||||
|
MatchName string // Match or event name
|
||||||
|
HomeTeam string // Home team name (if available)
|
||||||
|
AwayTeam string // Away team name (can be empty/null)
|
||||||
|
HomeTeamID string // Home team ID
|
||||||
|
AwayTeamID string // Away team ID (can be empty/null)
|
||||||
|
HomeKitImage string // Kit or image for home team (optional)
|
||||||
|
AwayKitImage string // Kit or image for away team (optional)
|
||||||
|
LeagueID string // League ID
|
||||||
|
LeagueName string // League name
|
||||||
|
LeagueCC string // League country code
|
||||||
|
StartTime time.Time // Converted from "time" field in UNIX format
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
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"
|
||||||
|
// "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,7 +39,73 @@ func (s *Store) SaveEvent(ctx context.Context, e domain.Event) error {
|
||||||
Status: pgtype.Text{String: e.Status, Valid: true},
|
Status: pgtype.Text{String: e.Status, Valid: true},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
func (s *Store) SaveUpcomingEvent(ctx context.Context, e domain.UpcomingEvent) error {
|
||||||
|
return s.queries.InsertUpcomingEvent(ctx, dbgen.InsertUpcomingEventParams{
|
||||||
|
ID: e.ID,
|
||||||
|
SportID: pgtype.Text{String: e.SportID, Valid: true},
|
||||||
|
MatchName: pgtype.Text{String: e.MatchName, Valid: true},
|
||||||
|
HomeTeam: pgtype.Text{String: e.HomeTeam, Valid: true},
|
||||||
|
AwayTeam: pgtype.Text{String: e.AwayTeam, Valid: true},
|
||||||
|
HomeTeamID: pgtype.Text{String: e.HomeTeamID, Valid: true},
|
||||||
|
AwayTeamID: pgtype.Text{String: e.AwayTeamID, Valid: true},
|
||||||
|
HomeKitImage: pgtype.Text{String: e.HomeKitImage, Valid: true},
|
||||||
|
AwayKitImage: pgtype.Text{String: e.AwayKitImage, Valid: true},
|
||||||
|
LeagueID: pgtype.Text{String: e.LeagueID, Valid: true},
|
||||||
|
LeagueName: pgtype.Text{String: e.LeagueName, Valid: true},
|
||||||
|
LeagueCc: pgtype.Text{String: e.LeagueCC, Valid: true},
|
||||||
|
StartTime: pgtype.Timestamp{Time: e.StartTime, Valid: true},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) {
|
func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) {
|
||||||
return s.queries.ListLiveEvents(ctx)
|
return s.queries.ListLiveEvents(ctx)
|
||||||
}
|
}
|
||||||
|
func (s *Store) GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) {
|
||||||
|
events, err := s.queries.GetAllUpcomingEvents(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
upcomingEvents := make([]domain.UpcomingEvent, len(events))
|
||||||
|
for i, e := range events {
|
||||||
|
upcomingEvents[i] = domain.UpcomingEvent{
|
||||||
|
ID: e.ID,
|
||||||
|
SportID: e.SportID.String,
|
||||||
|
MatchName: e.MatchName.String,
|
||||||
|
HomeTeam: e.HomeTeam.String,
|
||||||
|
AwayTeam: e.AwayTeam.String,
|
||||||
|
HomeTeamID: e.HomeTeamID.String,
|
||||||
|
AwayTeamID: e.AwayTeamID.String,
|
||||||
|
HomeKitImage: e.HomeKitImage.String,
|
||||||
|
AwayKitImage: e.AwayKitImage.String,
|
||||||
|
LeagueID: e.LeagueID.String,
|
||||||
|
LeagueName: e.LeagueName.String,
|
||||||
|
LeagueCC: e.LeagueCc.String,
|
||||||
|
StartTime: e.StartTime.Time.UTC(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return upcomingEvents, nil
|
||||||
|
}
|
||||||
|
func (s *Store) GetUpcomingEventByID(ctx context.Context, ID string) (domain.UpcomingEvent, error) {
|
||||||
|
event, err := s.queries.GetUpcomingByID(ctx, ID)
|
||||||
|
if err != nil {
|
||||||
|
return domain.UpcomingEvent{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.UpcomingEvent{
|
||||||
|
ID: event.ID,
|
||||||
|
SportID: event.SportID.String,
|
||||||
|
MatchName: event.MatchName.String,
|
||||||
|
HomeTeam: event.HomeTeam.String,
|
||||||
|
AwayTeam: event.AwayTeam.String,
|
||||||
|
HomeTeamID: event.HomeTeamID.String,
|
||||||
|
AwayTeamID: event.AwayTeamID.String,
|
||||||
|
HomeKitImage: event.HomeKitImage.String,
|
||||||
|
AwayKitImage: event.AwayKitImage.String,
|
||||||
|
LeagueID: event.LeagueID.String,
|
||||||
|
LeagueName: event.LeagueName.String,
|
||||||
|
LeagueCC: event.LeagueCc.String,
|
||||||
|
StartTime: event.StartTime.Time.UTC(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
package event
|
package event
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
FetchLiveEvents(ctx context.Context) error
|
FetchLiveEvents(ctx context.Context) error
|
||||||
FetchUpcomingEvents(ctx context.Context) error
|
FetchUpcomingEvents(ctx context.Context) error
|
||||||
|
GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error)
|
||||||
|
GetUpcomingEventByID(ctx context.Context, ID string) (domain.UpcomingEvent, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||||
|
// "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
|
|
@ -96,65 +98,66 @@ 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, 13, 78, 18, 91, 16, 17, 14, 12, 3, 2, 4, 83, 15, 92, 94, 8, 19, 36, 66, 9, 75, 90, 95, 110, 107, 151, 162, 148}
|
sportIDs := []int{1, 13, 78, 18, 91, 16, 17, 14, 12, 3, 2, 4, 83, 15, 92, 94, 8, 19, 36, 66, 9, 75, 90, 95, 110, 107, 151, 162, 148}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for _, sportID := range sportIDs {
|
for _, sportID := range sportIDs {
|
||||||
wg.Add(1)
|
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s", sportID, s.token)
|
||||||
go func(sportID int) {
|
resp, err := http.Get(url)
|
||||||
defer wg.Done()
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s", sportID, s.token)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
resp, err := http.Get(url)
|
var data struct {
|
||||||
if err != nil {
|
Success int `json:"success"`
|
||||||
fmt.Printf(" Failed request for upcoming sport_id=%d: %v\n", sportID, err)
|
Results []struct {
|
||||||
return
|
ID string `json:"id"`
|
||||||
}
|
SportID string `json:"sport_id"`
|
||||||
defer resp.Body.Close()
|
Time string `json:"time"`
|
||||||
|
League struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"league"`
|
||||||
|
Home struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"home"`
|
||||||
|
Away *struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"away"`
|
||||||
|
} `json:"results"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
for _, ev := range data.Results {
|
||||||
|
startUnix, _ := strconv.ParseInt(ev.Time, 10, 64)
|
||||||
var data struct {
|
event := domain.UpcomingEvent{
|
||||||
Success int `json:"success"`
|
ID: ev.ID,
|
||||||
Results [][]map[string]interface{} `json:"results"`
|
SportID: ev.SportID,
|
||||||
}
|
MatchName: ev.Home.Name,
|
||||||
if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 {
|
HomeTeam: ev.Home.Name,
|
||||||
fmt.Printf(" Decode failed for upcoming sport_id=%d\nRaw: %s\n", sportID, string(body))
|
AwayTeam: "", // handle nil safely
|
||||||
return
|
HomeTeamID: ev.Home.ID,
|
||||||
|
AwayTeamID: "",
|
||||||
|
HomeKitImage: "",
|
||||||
|
AwayKitImage: "",
|
||||||
|
LeagueID: ev.League.ID,
|
||||||
|
LeagueName: ev.League.Name,
|
||||||
|
LeagueCC: "",
|
||||||
|
StartTime: time.Unix(startUnix, 0).UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, group := range data.Results {
|
if ev.Away != nil {
|
||||||
for _, ev := range group {
|
event.AwayTeam = ev.Away.Name
|
||||||
if getString(ev["type"]) != "EV" {
|
event.AwayTeamID = ev.Away.ID
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
event := domain.Event{
|
|
||||||
ID: getString(ev["ID"]),
|
|
||||||
SportID: fmt.Sprintf("%d", sportID),
|
|
||||||
MatchName: getString(ev["NA"]),
|
|
||||||
HomeTeamID: getString(ev["HT"]),
|
|
||||||
AwayTeamID: getString(ev["AT"]),
|
|
||||||
HomeKitImage: getString(ev["K1"]),
|
|
||||||
AwayKitImage: getString(ev["K2"]),
|
|
||||||
LeagueID: getString(ev["C2"]),
|
|
||||||
LeagueName: getString(ev["CT"]),
|
|
||||||
LeagueCC: getString(ev["CB"]),
|
|
||||||
StartTime: time.Now().UTC().Format(time.RFC3339),
|
|
||||||
IsLive: false,
|
|
||||||
Status: "upcoming",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.store.SaveEvent(ctx, event); err != nil {
|
|
||||||
fmt.Printf(" Could not store upcoming event [id=%s]: %v\n", event.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}(sportID)
|
|
||||||
|
_ = s.store.SaveUpcomingEvent(ctx, event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
fmt.Println(" All upcoming events fetched and stored.")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,4 +173,11 @@ func getInt(v interface{}) int {
|
||||||
return int(f)
|
return int(f)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
func (s *service) GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) {
|
||||||
|
return s.store.GetAllUpcomingEvents(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) GetUpcomingEventByID(ctx context.Context, ID string) (domain.UpcomingEvent, error) {
|
||||||
|
return s.store.GetUpcomingEventByID(ctx, ID)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
|
||||||
|
|
@ -37,7 +38,8 @@ type App struct {
|
||||||
validator *customvalidator.CustomValidator
|
validator *customvalidator.CustomValidator
|
||||||
JwtConfig jwtutil.JwtConfig
|
JwtConfig jwtutil.JwtConfig
|
||||||
Logger *slog.Logger
|
Logger *slog.Logger
|
||||||
prematchSvc *odds.ServiceImpl
|
prematchSvc *odds.ServiceImpl
|
||||||
|
eventSvc event.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApp(
|
func NewApp(
|
||||||
|
|
@ -53,6 +55,7 @@ func NewApp(
|
||||||
branchSvc *branch.Service,
|
branchSvc *branch.Service,
|
||||||
notidicationStore notificationservice.NotificationStore,
|
notidicationStore notificationservice.NotificationStore,
|
||||||
prematchSvc *odds.ServiceImpl,
|
prematchSvc *odds.ServiceImpl,
|
||||||
|
eventSvc event.Service,
|
||||||
) *App {
|
) *App {
|
||||||
app := fiber.New(fiber.Config{
|
app := fiber.New(fiber.Config{
|
||||||
CaseSensitive: true,
|
CaseSensitive: true,
|
||||||
|
|
@ -82,7 +85,8 @@ func NewApp(
|
||||||
branchSvc: branchSvc,
|
branchSvc: branchSvc,
|
||||||
NotidicationStore: notidicationStore,
|
NotidicationStore: notidicationStore,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
prematchSvc: prematchSvc,
|
prematchSvc: prematchSvc,
|
||||||
|
eventSvc: eventSvc,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.initAppRoutes()
|
s.initAppRoutes()
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,14 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
|
||||||
}{
|
}{
|
||||||
|
|
||||||
{
|
{
|
||||||
spec: "0 0 * * * *", // Every hour
|
spec: "0 0 * * * *", // Every hour at minute 0 and second 0
|
||||||
task: func() {
|
task: func() {
|
||||||
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
|
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
|
||||||
log.Printf("FetchUpcomingEvents error: %v", err)
|
log.Printf("FetchUpcomingEvents error: %v", err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
spec: "*/5 * * * * *", // Every 5 seconds
|
spec: "*/5 * * * * *", // Every 5 seconds
|
||||||
task: func() {
|
task: func() {
|
||||||
|
|
@ -37,8 +36,8 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
spec: "0 * * * * *", // Every 1 minute
|
spec: "0 0 * * * *", // Every hour at minute 0 and second 0
|
||||||
task: func() {
|
task: func() {
|
||||||
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
|
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
|
||||||
log.Printf("FetchNonLiveOdds error: %v", err)
|
log.Printf("FetchNonLiveOdds error: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
)
|
)
|
||||||
|
|
@ -77,4 +78,48 @@ func GetRawOddsByID(logger *slog.Logger, prematchSvc *odds.ServiceImpl) fiber.Ha
|
||||||
|
|
||||||
return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", rawOdds, nil)
|
return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", rawOdds, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary Retrieve all upcoming events
|
||||||
|
// @Description Retrieve all upcoming events from the database
|
||||||
|
// @Tags prematch
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} domain.UpcomingEvent
|
||||||
|
// @Failure 500 {object} response.APIResponse
|
||||||
|
// @Router /prematch/events [get]
|
||||||
|
func GetAllUpcomingEvents(logger *slog.Logger, eventSvc event.Service) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
events, err := eventSvc.GetAllUpcomingEvents(c.Context())
|
||||||
|
if err != nil {
|
||||||
|
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve all upcoming events", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.WriteJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", events, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @Summary Retrieve an upcoming by ID
|
||||||
|
// @Description Retrieve an upcoming event by ID
|
||||||
|
// @Tags prematch
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "ID"
|
||||||
|
// @Success 200 {object} domain.UpcomingEvent
|
||||||
|
// @Failure 400 {object} response.APIResponse
|
||||||
|
// @Failure 500 {object} response.APIResponse
|
||||||
|
// @Router /prematch/events/{id} [get]
|
||||||
|
func GetUpcomingEventByID(logger *slog.Logger, eventSvc event.Service) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
id := c.Params("id")
|
||||||
|
if id == "" {
|
||||||
|
return response.WriteJSON(c, fiber.StatusBadRequest, "Missing id", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
event, err := eventSvc.GetUpcomingEventByID(c.Context(), id)
|
||||||
|
if err != nil {
|
||||||
|
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve upcoming event", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.WriteJSON(c, fiber.StatusOK, "Upcoming event retrieved successfully", event, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ func (a *App) initAppRoutes() {
|
||||||
a.fiber.Get("/prematch/odds/:event_id", handlers.GetPrematchOdds(a.logger, a.prematchSvc))
|
a.fiber.Get("/prematch/odds/:event_id", handlers.GetPrematchOdds(a.logger, a.prematchSvc))
|
||||||
a.fiber.Get("/prematch/odds", handlers.GetALLPrematchOdds(a.logger, a.prematchSvc))
|
a.fiber.Get("/prematch/odds", handlers.GetALLPrematchOdds(a.logger, a.prematchSvc))
|
||||||
a.fiber.Get("/prematch/odds/raw/:raw_odds_id", handlers.GetRawOddsByID(a.logger, a.prematchSvc))
|
a.fiber.Get("/prematch/odds/raw/:raw_odds_id", handlers.GetRawOddsByID(a.logger, a.prematchSvc))
|
||||||
|
|
||||||
|
a.fiber.Get("/prematch/events/:id", handlers.GetUpcomingEventByID(a.logger, a.eventSvc))
|
||||||
|
a.fiber.Get("/prematch/events", handlers.GetAllUpcomingEvents(a.logger, a.eventSvc))
|
||||||
// Swagger
|
// Swagger
|
||||||
a.fiber.Get("/swagger/*", fiberSwagger.FiberWrapHandler())
|
a.fiber.Get("/swagger/*", fiberSwagger.FiberWrapHandler())
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user