feat: result checker fixed for market flags
This commit is contained in:
parent
90aee0c470
commit
acf54d4de7
63
README.md
63
README.md
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
# Directory Structure
|
# Directory Structure
|
||||||
|
|
||||||
|
├── .vscode
|
||||||
|
│ ├── settings.json
|
||||||
├── cmd
|
├── cmd
|
||||||
│ ├── main.go
|
│ ├── main.go
|
||||||
├── db
|
├── db
|
||||||
|
|
@ -12,16 +14,24 @@
|
||||||
│ │ ├── 000002_notification.up.sql
|
│ │ ├── 000002_notification.up.sql
|
||||||
│ │ ├── 000003_referal.down.sql
|
│ │ ├── 000003_referal.down.sql
|
||||||
│ │ ├── 000003_referal.up.sql
|
│ │ ├── 000003_referal.up.sql
|
||||||
|
│ │ ├── 000004_virtual_game_Sessios.down.sql
|
||||||
|
│ │ ├── 000004_virtual_game_Sessios.up.sql
|
||||||
│ └── query
|
│ └── query
|
||||||
│ ├── auth.sql
|
│ ├── auth.sql
|
||||||
│ ├── bet.sql
|
│ ├── bet.sql
|
||||||
|
│ ├── branch.sql
|
||||||
|
│ ├── company.sql
|
||||||
|
│ ├── events.sql
|
||||||
│ ├── notification.sql
|
│ ├── notification.sql
|
||||||
|
│ ├── odds.sql
|
||||||
│ ├── otp.sql
|
│ ├── otp.sql
|
||||||
│ ├── referal.sql
|
│ ├── referal.sql
|
||||||
|
│ ├── result.sql
|
||||||
│ ├── ticket.sql
|
│ ├── ticket.sql
|
||||||
│ ├── transactions.sql
|
│ ├── transactions.sql
|
||||||
│ ├── transfer.sql
|
│ ├── transfer.sql
|
||||||
│ ├── user.sql
|
│ ├── user.sql
|
||||||
|
│ ├── virtual_games.sql
|
||||||
│ ├── wallet.sql
|
│ ├── wallet.sql
|
||||||
├── docs
|
├── docs
|
||||||
│ ├── docs.go
|
│ ├── docs.go
|
||||||
|
|
@ -31,26 +41,37 @@
|
||||||
│ └── db
|
│ └── db
|
||||||
│ ├── auth.sql.go
|
│ ├── auth.sql.go
|
||||||
│ ├── bet.sql.go
|
│ ├── bet.sql.go
|
||||||
|
│ ├── branch.sql.go
|
||||||
|
│ ├── company.sql.go
|
||||||
|
│ ├── copyfrom.go
|
||||||
│ ├── db.go
|
│ ├── db.go
|
||||||
|
│ ├── events.sql.go
|
||||||
│ ├── models.go
|
│ ├── models.go
|
||||||
│ ├── notification.sql.go
|
│ ├── notification.sql.go
|
||||||
|
│ ├── odds.sql.go
|
||||||
│ ├── otp.sql.go
|
│ ├── otp.sql.go
|
||||||
│ ├── referal.sql.go
|
│ ├── referal.sql.go
|
||||||
|
│ ├── result.sql.go
|
||||||
│ ├── ticket.sql.go
|
│ ├── ticket.sql.go
|
||||||
│ ├── transactions.sql.go
|
│ ├── transactions.sql.go
|
||||||
│ ├── transfer.sql.go
|
│ ├── transfer.sql.go
|
||||||
│ ├── user.sql.go
|
│ ├── user.sql.go
|
||||||
|
│ ├── virtual_games.sql.go
|
||||||
│ ├── wallet.sql.go
|
│ ├── wallet.sql.go
|
||||||
└── internal
|
└── internal
|
||||||
├── config
|
├── config
|
||||||
│ ├── config.go
|
│ ├── config.go
|
||||||
├── domain
|
├── domain
|
||||||
│ ├── auth.go
|
│ ├── auth.go
|
||||||
|
│ ├── bank.go
|
||||||
│ ├── bet.go
|
│ ├── bet.go
|
||||||
│ ├── branch.go
|
│ ├── branch.go
|
||||||
|
│ ├── chapa.go
|
||||||
│ ├── common.go
|
│ ├── common.go
|
||||||
|
│ ├── company.go
|
||||||
│ ├── event.go
|
│ ├── event.go
|
||||||
│ ├── notification.go
|
│ ├── notification.go
|
||||||
|
│ ├── odds.go
|
||||||
│ ├── otp.go
|
│ ├── otp.go
|
||||||
│ ├── referal.go
|
│ ├── referal.go
|
||||||
│ ├── role.go
|
│ ├── role.go
|
||||||
|
|
@ -58,6 +79,7 @@
|
||||||
│ ├── transaction.go
|
│ ├── transaction.go
|
||||||
│ ├── transfer.go
|
│ ├── transfer.go
|
||||||
│ ├── user.go
|
│ ├── user.go
|
||||||
|
│ ├── virtual_game.go
|
||||||
│ ├── wallet.go
|
│ ├── wallet.go
|
||||||
├── logger
|
├── logger
|
||||||
│ ├── logger.go
|
│ ├── logger.go
|
||||||
|
|
@ -72,14 +94,20 @@
|
||||||
├── repository
|
├── repository
|
||||||
│ ├── auth.go
|
│ ├── auth.go
|
||||||
│ ├── bet.go
|
│ ├── bet.go
|
||||||
|
│ ├── branch.go
|
||||||
|
│ ├── company.go
|
||||||
|
│ ├── event.go
|
||||||
│ ├── notification.go
|
│ ├── notification.go
|
||||||
|
│ ├── odds.go
|
||||||
│ ├── otp.go
|
│ ├── otp.go
|
||||||
│ ├── referal.go
|
│ ├── referal.go
|
||||||
|
│ ├── result.go
|
||||||
│ ├── store.go
|
│ ├── store.go
|
||||||
│ ├── ticket.go
|
│ ├── ticket.go
|
||||||
│ ├── transaction.go
|
│ ├── transaction.go
|
||||||
│ ├── transfer.go
|
│ ├── transfer.go
|
||||||
│ ├── user.go
|
│ ├── user.go
|
||||||
|
│ ├── virtual_game.go
|
||||||
│ ├── wallet.go
|
│ ├── wallet.go
|
||||||
├── services
|
├── services
|
||||||
│ ├── authentication
|
│ ├── authentication
|
||||||
|
|
@ -89,12 +117,27 @@
|
||||||
│ ├── bet
|
│ ├── bet
|
||||||
│ │ ├── port.go
|
│ │ ├── port.go
|
||||||
│ │ ├── service.go
|
│ │ ├── service.go
|
||||||
|
│ ├── branch
|
||||||
|
│ │ ├── port.go
|
||||||
|
│ │ ├── service.go
|
||||||
|
│ ├── company
|
||||||
|
│ │ ├── port.go
|
||||||
|
│ │ ├── service.go
|
||||||
|
│ ├── event
|
||||||
|
│ │ ├── port.go
|
||||||
|
│ │ ├── service.go
|
||||||
│ ├── notfication
|
│ ├── notfication
|
||||||
│ │ ├── port.go
|
│ │ ├── port.go
|
||||||
│ │ ├── service.go
|
│ │ ├── service.go
|
||||||
|
│ ├── odds
|
||||||
|
│ │ ├── port.go
|
||||||
|
│ │ ├── service.go
|
||||||
│ ├── referal
|
│ ├── referal
|
||||||
│ │ ├── port.go
|
│ │ ├── port.go
|
||||||
│ │ ├── service.go
|
│ │ ├── service.go
|
||||||
|
│ ├── result
|
||||||
|
│ │ ├── port.go
|
||||||
|
│ │ ├── service.go
|
||||||
│ ├── sportsbook
|
│ ├── sportsbook
|
||||||
│ │ ├── events.go
|
│ │ ├── events.go
|
||||||
│ │ ├── odds.go
|
│ │ ├── odds.go
|
||||||
|
|
@ -105,30 +148,40 @@
|
||||||
│ ├── transaction
|
│ ├── transaction
|
||||||
│ │ ├── port.go
|
│ │ ├── port.go
|
||||||
│ │ ├── service.go
|
│ │ ├── service.go
|
||||||
│ ├── transfer
|
|
||||||
│ │ ├── chapa.go
|
|
||||||
│ │ ├── port.go
|
|
||||||
│ │ ├── service.go
|
|
||||||
│ ├── user
|
│ ├── user
|
||||||
│ │ ├── common.go
|
│ │ ├── common.go
|
||||||
|
│ │ ├── direct.go
|
||||||
│ │ ├── port.go
|
│ │ ├── port.go
|
||||||
│ │ ├── register.go
|
│ │ ├── register.go
|
||||||
│ │ ├── reset.go
|
│ │ ├── reset.go
|
||||||
│ │ ├── service.go
|
│ │ ├── service.go
|
||||||
│ │ ├── user.go
|
│ │ ├── user.go
|
||||||
|
│ ├── virtualGame
|
||||||
|
│ │ ├── port.go
|
||||||
|
│ │ ├── service.go
|
||||||
│ └── wallet
|
│ └── wallet
|
||||||
|
│ ├── chapa.go
|
||||||
│ ├── port.go
|
│ ├── port.go
|
||||||
│ ├── service.go
|
│ ├── service.go
|
||||||
|
│ ├── transfer.go
|
||||||
|
│ ├── wallet.go
|
||||||
└── web_server
|
└── web_server
|
||||||
├── handlers
|
├── handlers
|
||||||
│ ├── auth_handler.go
|
│ ├── auth_handler.go
|
||||||
│ ├── bet_handler.go
|
│ ├── bet_handler.go
|
||||||
|
│ ├── branch_handler.go
|
||||||
|
│ ├── cashier.go
|
||||||
|
│ ├── company_handler.go
|
||||||
│ ├── handlers.go
|
│ ├── handlers.go
|
||||||
|
│ ├── manager.go
|
||||||
│ ├── notification_handler.go
|
│ ├── notification_handler.go
|
||||||
|
│ ├── prematch.go
|
||||||
│ ├── referal_handlers.go
|
│ ├── referal_handlers.go
|
||||||
│ ├── ticket_handler.go
|
│ ├── ticket_handler.go
|
||||||
│ ├── transaction_handler.go
|
│ ├── transaction_handler.go
|
||||||
|
│ ├── transfer_handler.go
|
||||||
│ ├── user.go
|
│ ├── user.go
|
||||||
|
│ ├── virtual_games_hadlers.go
|
||||||
│ ├── wallet_handler.go
|
│ ├── wallet_handler.go
|
||||||
├── jwt
|
├── jwt
|
||||||
│ ├── jwt.go
|
│ ├── jwt.go
|
||||||
|
|
@ -138,10 +191,10 @@
|
||||||
└── validator
|
└── validator
|
||||||
├── validatord.go
|
├── validatord.go
|
||||||
├── app.go
|
├── app.go
|
||||||
|
├── cron.go
|
||||||
├── middleware.go
|
├── middleware.go
|
||||||
├── routes.go
|
├── routes.go
|
||||||
├── .air.toml
|
├── .air.toml
|
||||||
├── .env
|
|
||||||
├── .gitignore
|
├── .gitignore
|
||||||
├── README.md
|
├── README.md
|
||||||
├── compose.db.yaml
|
├── compose.db.yaml
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ func main() {
|
||||||
|
|
||||||
eventSvc := event.New(cfg.Bet365Token, store)
|
eventSvc := event.New(cfg.Bet365Token, store)
|
||||||
oddsSvc := odds.New(cfg.Bet365Token, store)
|
oddsSvc := odds.New(cfg.Bet365Token, store)
|
||||||
resultSvc := result.NewService(cfg.Bet365Token,store)
|
resultSvc := result.NewService(store, cfg, logger)
|
||||||
ticketSvc := ticket.NewService(store)
|
ticketSvc := ticket.NewService(store)
|
||||||
betSvc := bet.NewService(store)
|
betSvc := bet.NewService(store)
|
||||||
walletSvc := wallet.NewService(store, store)
|
walletSvc := wallet.NewService(store, store)
|
||||||
|
|
@ -92,7 +92,7 @@ func main() {
|
||||||
JwtAccessKey: cfg.JwtKey,
|
JwtAccessKey: cfg.JwtKey,
|
||||||
JwtAccessExpiry: cfg.AccessExpiry,
|
JwtAccessExpiry: cfg.AccessExpiry,
|
||||||
}, userSvc,
|
}, userSvc,
|
||||||
ticketSvc, betSvc, walletSvc, transactionSvc, branchSvc, companySvc, notificationSvc, oddsSvc, eventSvc, referalSvc, virtualGameSvc)
|
ticketSvc, betSvc, walletSvc, transactionSvc, branchSvc, companySvc, notificationSvc, oddsSvc, eventSvc, referalSvc, virtualGameSvc, resultSvc)
|
||||||
logger.Info("Starting server", "port", cfg.Port)
|
logger.Info("Starting server", "port", cfg.Port)
|
||||||
|
|
||||||
if err := app.Run(); err != nil {
|
if err := app.Run(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -386,13 +386,3 @@ VALUES (
|
||||||
CURRENT_TIMESTAMP,
|
CURRENT_TIMESTAMP,
|
||||||
CURRENT_TIMESTAMP
|
CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
--------------------------------------------------Bet365 Data Fetching + Event Managment------------------------------------------------
|
|
||||||
CREATE TABLE results (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
event_id TEXT UNIQUE,
|
|
||||||
full_time_score TEXT,
|
|
||||||
half_time_score TEXT,
|
|
||||||
ss TEXT,
|
|
||||||
scores JSONB,
|
|
||||||
fetched_at TIMESTAMPTZ DEFAULT now()
|
|
||||||
);
|
|
||||||
1
db/migrations/000005_result_checker.down.sql
Normal file
1
db/migrations/000005_result_checker.down.sql
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE IF EXISTS results;
|
||||||
15
db/migrations/000005_result_checker.up.sql
Normal file
15
db/migrations/000005_result_checker.up.sql
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS results (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
bet_outcome_id BIGINT NOT NULL,
|
||||||
|
event_id BIGINT NOT NULL,
|
||||||
|
odd_id BIGINT NOT NULL,
|
||||||
|
market_id BIGINT NOT NULL,
|
||||||
|
status INT NOT NULL,
|
||||||
|
score VARCHAR(255),
|
||||||
|
full_time_score VARCHAR(255),
|
||||||
|
half_time_score VARCHAR(255),
|
||||||
|
ss VARCHAR(255),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (bet_outcome_id) REFERENCES bet_outcomes (id)
|
||||||
|
);
|
||||||
|
|
@ -1,8 +1,53 @@
|
||||||
-- name: InsertResult :one
|
-- name: CreateResult :one
|
||||||
INSERT INTO results (event_id, full_time_score, half_time_score, ss, scores)
|
INSERT INTO results (
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
bet_outcome_id,
|
||||||
RETURNING id, event_id, full_time_score, half_time_score, ss, scores, fetched_at;
|
event_id,
|
||||||
|
odd_id,
|
||||||
|
market_id,
|
||||||
|
status,
|
||||||
|
score,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
) VALUES (
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3,
|
||||||
|
$4,
|
||||||
|
$5,
|
||||||
|
$6,
|
||||||
|
CURRENT_TIMESTAMP,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
) RETURNING *;
|
||||||
|
|
||||||
|
-- name: InsertResult :exec
|
||||||
|
INSERT INTO results (
|
||||||
|
bet_outcome_id,
|
||||||
|
event_id,
|
||||||
|
odd_id,
|
||||||
|
market_id,
|
||||||
|
status,
|
||||||
|
score,
|
||||||
|
full_time_score,
|
||||||
|
half_time_score,
|
||||||
|
ss,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
) VALUES (
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3,
|
||||||
|
$4,
|
||||||
|
$5,
|
||||||
|
$6,
|
||||||
|
$7,
|
||||||
|
$8,
|
||||||
|
$9,
|
||||||
|
CURRENT_TIMESTAMP,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
-- name: GetResultByEventID :one
|
-- name: GetResultByBetOutcomeID :one
|
||||||
SELECT * FROM results WHERE event_id = $1;
|
SELECT * FROM results WHERE bet_outcome_id = $1;
|
||||||
|
|
||||||
|
-- name: GetPendingBetOutcomes :many
|
||||||
|
SELECT * FROM bet_outcomes WHERE status = 0 AND expires <= CURRENT_TIMESTAMP;
|
||||||
|
|
|
||||||
|
|
@ -270,13 +270,18 @@ type RefreshToken struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
ID int32 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
EventID pgtype.Text `json:"event_id"`
|
BetOutcomeID int64 `json:"bet_outcome_id"`
|
||||||
|
EventID int64 `json:"event_id"`
|
||||||
|
OddID int64 `json:"odd_id"`
|
||||||
|
MarketID int64 `json:"market_id"`
|
||||||
|
Status int32 `json:"status"`
|
||||||
|
Score pgtype.Text `json:"score"`
|
||||||
FullTimeScore pgtype.Text `json:"full_time_score"`
|
FullTimeScore pgtype.Text `json:"full_time_score"`
|
||||||
HalfTimeScore pgtype.Text `json:"half_time_score"`
|
HalfTimeScore pgtype.Text `json:"half_time_score"`
|
||||||
Ss pgtype.Text `json:"ss"`
|
Ss pgtype.Text `json:"ss"`
|
||||||
Scores []byte `json:"scores"`
|
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||||
FetchedAt pgtype.Timestamptz `json:"fetched_at"`
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SupportedOperation struct {
|
type SupportedOperation struct {
|
||||||
|
|
|
||||||
|
|
@ -11,56 +11,178 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
const GetResultByEventID = `-- name: GetResultByEventID :one
|
const CreateResult = `-- name: CreateResult :one
|
||||||
SELECT id, event_id, full_time_score, half_time_score, ss, scores, fetched_at FROM results WHERE event_id = $1
|
INSERT INTO results (
|
||||||
|
bet_outcome_id,
|
||||||
|
event_id,
|
||||||
|
odd_id,
|
||||||
|
market_id,
|
||||||
|
status,
|
||||||
|
score,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
) VALUES (
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3,
|
||||||
|
$4,
|
||||||
|
$5,
|
||||||
|
$6,
|
||||||
|
CURRENT_TIMESTAMP,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
) RETURNING id, bet_outcome_id, event_id, odd_id, market_id, status, score, full_time_score, half_time_score, ss, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetResultByEventID(ctx context.Context, eventID pgtype.Text) (Result, error) {
|
type CreateResultParams struct {
|
||||||
row := q.db.QueryRow(ctx, GetResultByEventID, eventID)
|
BetOutcomeID int64 `json:"bet_outcome_id"`
|
||||||
|
EventID int64 `json:"event_id"`
|
||||||
|
OddID int64 `json:"odd_id"`
|
||||||
|
MarketID int64 `json:"market_id"`
|
||||||
|
Status int32 `json:"status"`
|
||||||
|
Score pgtype.Text `json:"score"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateResult(ctx context.Context, arg CreateResultParams) (Result, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CreateResult,
|
||||||
|
arg.BetOutcomeID,
|
||||||
|
arg.EventID,
|
||||||
|
arg.OddID,
|
||||||
|
arg.MarketID,
|
||||||
|
arg.Status,
|
||||||
|
arg.Score,
|
||||||
|
)
|
||||||
var i Result
|
var i Result
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
|
&i.BetOutcomeID,
|
||||||
&i.EventID,
|
&i.EventID,
|
||||||
|
&i.OddID,
|
||||||
|
&i.MarketID,
|
||||||
|
&i.Status,
|
||||||
|
&i.Score,
|
||||||
&i.FullTimeScore,
|
&i.FullTimeScore,
|
||||||
&i.HalfTimeScore,
|
&i.HalfTimeScore,
|
||||||
&i.Ss,
|
&i.Ss,
|
||||||
&i.Scores,
|
&i.CreatedAt,
|
||||||
&i.FetchedAt,
|
&i.UpdatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const InsertResult = `-- name: InsertResult :one
|
const GetPendingBetOutcomes = `-- name: GetPendingBetOutcomes :many
|
||||||
INSERT INTO results (event_id, full_time_score, half_time_score, ss, scores)
|
SELECT id, bet_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires FROM bet_outcomes WHERE status = 0 AND expires <= CURRENT_TIMESTAMP
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
`
|
||||||
RETURNING id, event_id, full_time_score, half_time_score, ss, scores, fetched_at
|
|
||||||
|
func (q *Queries) GetPendingBetOutcomes(ctx context.Context) ([]BetOutcome, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetPendingBetOutcomes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []BetOutcome
|
||||||
|
for rows.Next() {
|
||||||
|
var i BetOutcome
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.BetID,
|
||||||
|
&i.EventID,
|
||||||
|
&i.OddID,
|
||||||
|
&i.HomeTeamName,
|
||||||
|
&i.AwayTeamName,
|
||||||
|
&i.MarketID,
|
||||||
|
&i.MarketName,
|
||||||
|
&i.Odd,
|
||||||
|
&i.OddName,
|
||||||
|
&i.OddHeader,
|
||||||
|
&i.OddHandicap,
|
||||||
|
&i.Status,
|
||||||
|
&i.Expires,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetResultByBetOutcomeID = `-- name: GetResultByBetOutcomeID :one
|
||||||
|
SELECT id, bet_outcome_id, event_id, odd_id, market_id, status, score, full_time_score, half_time_score, ss, created_at, updated_at FROM results WHERE bet_outcome_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetResultByBetOutcomeID(ctx context.Context, betOutcomeID int64) (Result, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetResultByBetOutcomeID, betOutcomeID)
|
||||||
|
var i Result
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.BetOutcomeID,
|
||||||
|
&i.EventID,
|
||||||
|
&i.OddID,
|
||||||
|
&i.MarketID,
|
||||||
|
&i.Status,
|
||||||
|
&i.Score,
|
||||||
|
&i.FullTimeScore,
|
||||||
|
&i.HalfTimeScore,
|
||||||
|
&i.Ss,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const InsertResult = `-- name: InsertResult :exec
|
||||||
|
INSERT INTO results (
|
||||||
|
bet_outcome_id,
|
||||||
|
event_id,
|
||||||
|
odd_id,
|
||||||
|
market_id,
|
||||||
|
status,
|
||||||
|
score,
|
||||||
|
full_time_score,
|
||||||
|
half_time_score,
|
||||||
|
ss,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
) VALUES (
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3,
|
||||||
|
$4,
|
||||||
|
$5,
|
||||||
|
$6,
|
||||||
|
$7,
|
||||||
|
$8,
|
||||||
|
$9,
|
||||||
|
CURRENT_TIMESTAMP,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
`
|
`
|
||||||
|
|
||||||
type InsertResultParams struct {
|
type InsertResultParams struct {
|
||||||
EventID pgtype.Text `json:"event_id"`
|
BetOutcomeID int64 `json:"bet_outcome_id"`
|
||||||
|
EventID int64 `json:"event_id"`
|
||||||
|
OddID int64 `json:"odd_id"`
|
||||||
|
MarketID int64 `json:"market_id"`
|
||||||
|
Status int32 `json:"status"`
|
||||||
|
Score pgtype.Text `json:"score"`
|
||||||
FullTimeScore pgtype.Text `json:"full_time_score"`
|
FullTimeScore pgtype.Text `json:"full_time_score"`
|
||||||
HalfTimeScore pgtype.Text `json:"half_time_score"`
|
HalfTimeScore pgtype.Text `json:"half_time_score"`
|
||||||
Ss pgtype.Text `json:"ss"`
|
Ss pgtype.Text `json:"ss"`
|
||||||
Scores []byte `json:"scores"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) InsertResult(ctx context.Context, arg InsertResultParams) (Result, error) {
|
func (q *Queries) InsertResult(ctx context.Context, arg InsertResultParams) error {
|
||||||
row := q.db.QueryRow(ctx, InsertResult,
|
_, err := q.db.Exec(ctx, InsertResult,
|
||||||
|
arg.BetOutcomeID,
|
||||||
arg.EventID,
|
arg.EventID,
|
||||||
|
arg.OddID,
|
||||||
|
arg.MarketID,
|
||||||
|
arg.Status,
|
||||||
|
arg.Score,
|
||||||
arg.FullTimeScore,
|
arg.FullTimeScore,
|
||||||
arg.HalfTimeScore,
|
arg.HalfTimeScore,
|
||||||
arg.Ss,
|
arg.Ss,
|
||||||
arg.Scores,
|
|
||||||
)
|
)
|
||||||
var i Result
|
return err
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.EventID,
|
|
||||||
&i.FullTimeScore,
|
|
||||||
&i.HalfTimeScore,
|
|
||||||
&i.Ss,
|
|
||||||
&i.Scores,
|
|
||||||
&i.FetchedAt,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,16 +36,3 @@ func (m Currency) String() string {
|
||||||
x = x / 100
|
x = x / 100
|
||||||
return fmt.Sprintf("$%.2f", x)
|
return fmt.Sprintf("$%.2f", x)
|
||||||
}
|
}
|
||||||
|
|
||||||
type OutcomeStatus int
|
|
||||||
|
|
||||||
const (
|
|
||||||
OUTCOME_STATUS_PENDING OutcomeStatus = iota
|
|
||||||
OUTCOME_STATUS_WIN
|
|
||||||
OUTCOME_STATUS_LOSS
|
|
||||||
OUTCOME_STATUS_ERROR
|
|
||||||
)
|
|
||||||
|
|
||||||
func (b OutcomeStatus) String() string {
|
|
||||||
return []string{"Pending", "Win", "Loss", "Error"}[b]
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -46,17 +46,7 @@ type MatchResult struct {
|
||||||
Status string
|
Status string
|
||||||
Scores map[string]map[string]string
|
Scores map[string]map[string]string
|
||||||
}
|
}
|
||||||
type Result struct {
|
|
||||||
EventID string
|
|
||||||
FullTimeScore string
|
|
||||||
HalfTimeScore string
|
|
||||||
SS string
|
|
||||||
Scores map[string]Score // "1": {"home": "0", "away": "1"}
|
|
||||||
}
|
|
||||||
type Score struct {
|
|
||||||
Home string `json:"home"`
|
|
||||||
Away string `json:"away"`
|
|
||||||
}
|
|
||||||
type Odds struct {
|
type Odds struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
EventID string `json:"event_id"`
|
EventID string `json:"event_id"`
|
||||||
|
|
|
||||||
105
internal/domain/result.go
Normal file
105
internal/domain/result.go
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResultResponse struct {
|
||||||
|
Success int `json:"success"`
|
||||||
|
Results []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
SportID string `json:"sport_id"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
TimeStatus string `json:"time_status"`
|
||||||
|
League struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CC string `json:"cc"`
|
||||||
|
} `json:"league"`
|
||||||
|
Home struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ImageID string `json:"image_id"`
|
||||||
|
CC string `json:"cc"`
|
||||||
|
} `json:"home"`
|
||||||
|
Away struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ImageID string `json:"image_id"`
|
||||||
|
CC string `json:"cc"`
|
||||||
|
} `json:"away"`
|
||||||
|
SS string `json:"ss"`
|
||||||
|
Scores struct {
|
||||||
|
FirstHalf Score `json:"1"`
|
||||||
|
SecondHalf Score `json:"2"`
|
||||||
|
} `json:"scores"`
|
||||||
|
Stats struct {
|
||||||
|
Attacks []string `json:"attacks"`
|
||||||
|
Corners []string `json:"corners"`
|
||||||
|
DangerousAttacks []string `json:"dangerous_attacks"`
|
||||||
|
Goals []string `json:"goals"`
|
||||||
|
OffTarget []string `json:"off_target"`
|
||||||
|
OnTarget []string `json:"on_target"`
|
||||||
|
Penalties []string `json:"penalties"`
|
||||||
|
PossessionRT []string `json:"possession_rt"`
|
||||||
|
RedCards []string `json:"redcards"`
|
||||||
|
Substitutions []string `json:"substitutions"`
|
||||||
|
YellowCards []string `json:"yellowcards"`
|
||||||
|
} `json:"stats"`
|
||||||
|
Extra struct {
|
||||||
|
HomePos string `json:"home_pos"`
|
||||||
|
AwayPos string `json:"away_pos"`
|
||||||
|
StadiumData map[string]string `json:"stadium_data"`
|
||||||
|
Round string `json:"round"`
|
||||||
|
} `json:"extra"`
|
||||||
|
Events []map[string]string `json:"events"`
|
||||||
|
HasLineup int `json:"has_lineup"`
|
||||||
|
ConfirmedAt string `json:"confirmed_at"`
|
||||||
|
Bet365ID string `json:"bet365_id"`
|
||||||
|
} `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Score struct {
|
||||||
|
Home string `json:"home"`
|
||||||
|
Away string `json:"away"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MarketConfig struct {
|
||||||
|
Sport string
|
||||||
|
MarketCategories map[string]bool
|
||||||
|
MarketTypes map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
ID int64
|
||||||
|
BetOutcomeID int64
|
||||||
|
EventID int64
|
||||||
|
OddID int64
|
||||||
|
MarketID int64
|
||||||
|
Status OutcomeStatus
|
||||||
|
Score string
|
||||||
|
FullTimeScore string
|
||||||
|
HalfTimeScore string
|
||||||
|
SS string
|
||||||
|
Scores map[string]Score
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateResult struct {
|
||||||
|
BetOutcomeID int64
|
||||||
|
EventID int64
|
||||||
|
OddID int64
|
||||||
|
MarketID int64
|
||||||
|
Status OutcomeStatus
|
||||||
|
Score string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutcomeStatus int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
OUTCOME_STATUS_PENDING OutcomeStatus = 0
|
||||||
|
OUTCOME_STATUS_WIN OutcomeStatus = 1
|
||||||
|
OUTCOME_STATUS_LOSS OutcomeStatus = 2
|
||||||
|
OUTCOME_STATUS_VOID OutcomeStatus = 3
|
||||||
|
)
|
||||||
|
|
@ -2,60 +2,99 @@ package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"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"
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Store) InsertResult(ctx context.Context, result domain.Result) error {
|
func convertDBResult(result dbgen.Result) domain.Result {
|
||||||
scoresJSON, err := json.Marshal(result.Scores)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to marshal Scores: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = s.queries.InsertResult(ctx, dbgen.InsertResultParams{
|
|
||||||
EventID: pgtype.Text{String: result.EventID, Valid: true},
|
|
||||||
FullTimeScore: pgtype.Text{String: result.FullTimeScore, Valid: true},
|
|
||||||
HalfTimeScore: pgtype.Text{String: result.HalfTimeScore, Valid: true},
|
|
||||||
Ss: pgtype.Text{String: result.SS, Valid: true},
|
|
||||||
Scores: scoresJSON,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to insert result: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) GetResultByEventID(ctx context.Context, eventID string) (domain.Result, error) {
|
|
||||||
eventIDText := pgtype.Text{String: eventID, Valid: true}
|
|
||||||
|
|
||||||
result, err := s.queries.GetResultByEventID(ctx, eventIDText)
|
|
||||||
if err != nil {
|
|
||||||
return domain.Result{}, fmt.Errorf("failed to get result by event ID: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawScores map[string]map[string]string
|
|
||||||
if err := json.Unmarshal(result.Scores, &rawScores); err != nil {
|
|
||||||
return domain.Result{}, fmt.Errorf("failed to unmarshal scores: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
scores := make(map[string]domain.Score)
|
scores := make(map[string]domain.Score)
|
||||||
for key, value := range rawScores {
|
|
||||||
scores[key] = domain.Score{
|
|
||||||
Home: value["home"],
|
|
||||||
Away: value["away"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return domain.Result{
|
return domain.Result{
|
||||||
EventID: result.EventID.String,
|
ID: result.ID,
|
||||||
|
BetOutcomeID: result.BetOutcomeID,
|
||||||
|
EventID: result.EventID,
|
||||||
|
OddID: result.OddID,
|
||||||
|
MarketID: result.MarketID,
|
||||||
|
Status: domain.OutcomeStatus(result.Status),
|
||||||
|
Score: result.Score.String,
|
||||||
FullTimeScore: result.FullTimeScore.String,
|
FullTimeScore: result.FullTimeScore.String,
|
||||||
HalfTimeScore: result.HalfTimeScore.String,
|
HalfTimeScore: result.HalfTimeScore.String,
|
||||||
SS: result.Ss.String,
|
SS: result.Ss.String,
|
||||||
Scores: scores,
|
Scores: scores,
|
||||||
}, nil
|
CreatedAt: result.CreatedAt.Time,
|
||||||
|
UpdatedAt: result.UpdatedAt.Time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertCreateResult(result domain.CreateResult) dbgen.CreateResultParams {
|
||||||
|
return dbgen.CreateResultParams{
|
||||||
|
BetOutcomeID: result.BetOutcomeID,
|
||||||
|
EventID: result.EventID,
|
||||||
|
OddID: result.OddID,
|
||||||
|
MarketID: result.MarketID,
|
||||||
|
Status: int32(result.Status),
|
||||||
|
Score: pgtype.Text{String: result.Score},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertResult(result domain.Result) dbgen.InsertResultParams {
|
||||||
|
return dbgen.InsertResultParams{
|
||||||
|
BetOutcomeID: result.BetOutcomeID,
|
||||||
|
EventID: result.EventID,
|
||||||
|
OddID: result.OddID,
|
||||||
|
MarketID: result.MarketID,
|
||||||
|
Status: int32(result.Status),
|
||||||
|
Score: pgtype.Text{String: result.Score},
|
||||||
|
FullTimeScore: pgtype.Text{String: result.FullTimeScore},
|
||||||
|
HalfTimeScore: pgtype.Text{String: result.HalfTimeScore},
|
||||||
|
Ss: pgtype.Text{String: result.SS},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) CreateResult(ctx context.Context, result domain.CreateResult) (domain.Result, error) {
|
||||||
|
dbResult, err := s.queries.CreateResult(ctx, convertCreateResult(result))
|
||||||
|
if err != nil {
|
||||||
|
return domain.Result{}, err
|
||||||
|
}
|
||||||
|
return convertDBResult(dbResult), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) InsertResult(ctx context.Context, result domain.Result) error {
|
||||||
|
return s.queries.InsertResult(ctx, convertResult(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetResultByBetOutcomeID(ctx context.Context, betOutcomeID int64) (domain.Result, error) {
|
||||||
|
dbResult, err := s.queries.GetResultByBetOutcomeID(ctx, betOutcomeID)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Result{}, err
|
||||||
|
}
|
||||||
|
return convertDBResult(dbResult), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetPendingBetOutcomes(ctx context.Context) ([]domain.BetOutcome, error) {
|
||||||
|
dbOutcomes, err := s.queries.GetPendingBetOutcomes(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
outcomes := make([]domain.BetOutcome, 0, len(dbOutcomes))
|
||||||
|
for _, dbOutcome := range dbOutcomes {
|
||||||
|
outcomes = append(outcomes, domain.BetOutcome{
|
||||||
|
ID: dbOutcome.ID,
|
||||||
|
BetID: dbOutcome.BetID,
|
||||||
|
EventID: dbOutcome.EventID,
|
||||||
|
OddID: dbOutcome.OddID,
|
||||||
|
HomeTeamName: dbOutcome.HomeTeamName,
|
||||||
|
AwayTeamName: dbOutcome.AwayTeamName,
|
||||||
|
MarketID: dbOutcome.MarketID,
|
||||||
|
MarketName: dbOutcome.MarketName,
|
||||||
|
Odd: dbOutcome.Odd,
|
||||||
|
OddName: dbOutcome.OddName,
|
||||||
|
OddHeader: dbOutcome.OddHeader,
|
||||||
|
OddHandicap: dbOutcome.OddHandicap,
|
||||||
|
Status: domain.OutcomeStatus(dbOutcome.Status),
|
||||||
|
Expires: dbOutcome.Expires.Time,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return outcomes, nil
|
||||||
}
|
}
|
||||||
|
|
@ -2,10 +2,9 @@ package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Store interface {
|
type ResultService interface {
|
||||||
InsertResult(ctx context.Context, result domain.Result) error
|
FetchAndProcessResults(ctx context.Context) error
|
||||||
GetResultByEventID(ctx context.Context, eventID string) (domain.Result, error)
|
FetchAndStoreResult(ctx context.Context, eventID string) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,65 +4,433 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service interface {
|
type Service struct {
|
||||||
FetchAndStoreResult(ctx context.Context, eventID string) error
|
repo *repository.Store
|
||||||
|
config *config.Config
|
||||||
|
logger *slog.Logger
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
func NewService(repo *repository.Store, cfg *config.Config, logger *slog.Logger) *Service {
|
||||||
token string
|
return &Service{
|
||||||
store *repository.Store
|
repo: repo,
|
||||||
}
|
config: cfg,
|
||||||
|
logger: logger,
|
||||||
func NewService(token string, store *repository.Store) Service {
|
client: &http.Client{Timeout: 10 * time.Second},
|
||||||
return &service{
|
|
||||||
token: token,
|
|
||||||
store: store,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) FetchAndStoreResult(ctx context.Context, eventID string) error {
|
var supportedMarkets = map[string]domain.MarketConfig{
|
||||||
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%s", s.token, eventID)
|
"football": {
|
||||||
|
Sport: "football",
|
||||||
|
MarketCategories: map[string]bool{
|
||||||
|
"main": true,
|
||||||
|
"asian_lines": true,
|
||||||
|
"goals": true,
|
||||||
|
"half": true,
|
||||||
|
},
|
||||||
|
MarketTypes: map[string]bool{
|
||||||
|
"full_time_result": true,
|
||||||
|
"double_chance": true,
|
||||||
|
"goals_over_under": true,
|
||||||
|
"correct_score": true,
|
||||||
|
"asian_handicap": true,
|
||||||
|
"goal_line": true,
|
||||||
|
"half_time_result": true,
|
||||||
|
"1st_half_asian_handicap": true,
|
||||||
|
"1st_half_goal_line": true,
|
||||||
|
"first_team_to_score": true,
|
||||||
|
"goals_odd_even": true,
|
||||||
|
"draw_no_bet": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
res, err := http.Get(url)
|
func (s *Service) FetchAndProcessResults(ctx context.Context) error {
|
||||||
|
outcomes, err := s.repo.GetPendingBetOutcomes(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get pending bet outcomes", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, outcome := range outcomes {
|
||||||
|
if outcome.Expires.After(time.Now()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s.fetchResult(ctx, outcome.EventID, outcome.OddID, outcome.MarketID, outcome)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to fetch result", "event_id", outcome.EventID, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.repo.CreateResult(ctx, domain.CreateResult{
|
||||||
|
BetOutcomeID: outcome.ID,
|
||||||
|
EventID: outcome.EventID,
|
||||||
|
OddID: outcome.OddID,
|
||||||
|
MarketID: outcome.MarketID,
|
||||||
|
Status: result.Status,
|
||||||
|
Score: result.Score,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to store result", "bet_outcome_id", outcome.ID, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.repo.UpdateBetOutcomeStatus(ctx, outcome.ID, result.Status)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update bet outcome status", "bet_outcome_id", outcome.ID, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) FetchAndStoreResult(ctx context.Context, eventID string) error {
|
||||||
|
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%s", s.config.Bet365Token, eventID)
|
||||||
|
|
||||||
|
res, err := s.client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to fetch result", "event_id", eventID, "error", err)
|
||||||
return fmt.Errorf("failed to fetch result: %w", err)
|
return fmt.Errorf("failed to fetch result: %w", err)
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
|
||||||
var apiResp struct {
|
if res.StatusCode != http.StatusOK {
|
||||||
Results []struct {
|
s.logger.Error("Unexpected status code", "event_id", eventID, "status_code", res.StatusCode)
|
||||||
SS string
|
return fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||||
Scores map[string]domain.Score `json:"scores"`
|
|
||||||
} `json:"results"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var apiResp domain.ResultResponse
|
||||||
if err := json.NewDecoder(res.Body).Decode(&apiResp); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&apiResp); err != nil {
|
||||||
|
s.logger.Error("Failed to decode result", "event_id", eventID, "error", err)
|
||||||
return fmt.Errorf("failed to decode result: %w", err)
|
return fmt.Errorf("failed to decode result: %w", err)
|
||||||
}
|
}
|
||||||
if len(apiResp.Results) == 0 {
|
|
||||||
|
if apiResp.Success != 1 || len(apiResp.Results) == 0 {
|
||||||
|
s.logger.Error("Invalid API response", "event_id", eventID)
|
||||||
return fmt.Errorf("no result returned from API")
|
return fmt.Errorf("no result returned from API")
|
||||||
}
|
}
|
||||||
|
|
||||||
r := apiResp.Results[0]
|
r := apiResp.Results[0]
|
||||||
|
if r.TimeStatus != "3" {
|
||||||
|
s.logger.Warn("Match not yet completed", "event_id", eventID)
|
||||||
|
return fmt.Errorf("match not yet completed")
|
||||||
|
}
|
||||||
|
|
||||||
|
eventIDInt, err := strconv.ParseInt(eventID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to parse event_id", "event_id", eventID, "error", err)
|
||||||
|
return fmt.Errorf("failed to parse event_id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
halfScore := ""
|
halfScore := ""
|
||||||
if s1, ok := r.Scores["1"]; ok {
|
if r.Scores.FirstHalf.Home != "" {
|
||||||
halfScore = fmt.Sprintf("%s-%s", s1.Home, s1.Away)
|
halfScore = fmt.Sprintf("%s-%s", r.Scores.FirstHalf.Home, r.Scores.FirstHalf.Away)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := domain.Result{
|
result := domain.Result{
|
||||||
EventID: eventID,
|
EventID: eventIDInt,
|
||||||
|
Status: domain.OUTCOME_STATUS_PENDING,
|
||||||
|
Score: r.SS,
|
||||||
FullTimeScore: r.SS,
|
FullTimeScore: r.SS,
|
||||||
HalfTimeScore: halfScore,
|
HalfTimeScore: halfScore,
|
||||||
SS: r.SS,
|
SS: r.SS,
|
||||||
Scores: r.Scores,
|
Scores: make(map[string]domain.Score),
|
||||||
|
}
|
||||||
|
for k, v := range map[string]domain.Score{
|
||||||
|
"1": r.Scores.FirstHalf,
|
||||||
|
"2": r.Scores.SecondHalf,
|
||||||
|
} {
|
||||||
|
result.Scores[k] = domain.Score{
|
||||||
|
Home: v.Home,
|
||||||
|
Away: v.Away,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.store.InsertResult(ctx, result)
|
return s.repo.InsertResult(ctx, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) fetchResult(ctx context.Context, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
|
||||||
|
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%d", s.config.Bet365Token, eventID)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to create request", "event_id", eventID, "error", err)
|
||||||
|
return domain.CreateResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to fetch result", "event_id", eventID, "error", err)
|
||||||
|
return domain.CreateResult{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
s.logger.Error("Unexpected status code", "event_id", eventID, "status_code", resp.StatusCode)
|
||||||
|
return domain.CreateResult{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultResp domain.ResultResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&resultResp); err != nil {
|
||||||
|
s.logger.Error("Failed to decode result", "event_id", eventID, "error", err)
|
||||||
|
return domain.CreateResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resultResp.Success != 1 || len(resultResp.Results) == 0 {
|
||||||
|
s.logger.Error("Invalid API response", "event_id", eventID)
|
||||||
|
return domain.CreateResult{}, fmt.Errorf("invalid API response")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := resultResp.Results[0]
|
||||||
|
if result.TimeStatus != "3" {
|
||||||
|
s.logger.Warn("Match not yet completed", "event_id", eventID)
|
||||||
|
return domain.CreateResult{}, fmt.Errorf("match not yet completed")
|
||||||
|
}
|
||||||
|
|
||||||
|
finalScore := parseScore(result.SS)
|
||||||
|
firstHalfScore := parseScore(fmt.Sprintf("%s-%s", result.Scores.FirstHalf.Home, result.Scores.FirstHalf.Away))
|
||||||
|
|
||||||
|
corners := parseStats(result.Stats.Corners)
|
||||||
|
status, err := s.evaluateOutcome(outcome, finalScore, firstHalfScore, corners, result.Events)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to evaluate outcome", "event_id", eventID, "market_id", marketID, "error", err)
|
||||||
|
return domain.CreateResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.CreateResult{
|
||||||
|
BetOutcomeID: 0,
|
||||||
|
EventID: eventID,
|
||||||
|
OddID: oddID,
|
||||||
|
MarketID: marketID,
|
||||||
|
Status: status,
|
||||||
|
Score: result.SS,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScore(scoreStr string) struct{ Home, Away int } {
|
||||||
|
parts := strings.Split(scoreStr, "-")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return struct{ Home, Away int }{0, 0}
|
||||||
|
}
|
||||||
|
home, _ := strconv.Atoi(strings.TrimSpace(parts[0]))
|
||||||
|
away, _ := strconv.Atoi(strings.TrimSpace(parts[1]))
|
||||||
|
return struct{ Home, Away int }{Home: home, Away: away}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStats(stats []string) struct{ Home, Away int } {
|
||||||
|
if len(stats) != 2 {
|
||||||
|
return struct{ Home, Away int }{0, 0}
|
||||||
|
}
|
||||||
|
home, _ := strconv.Atoi(stats[0])
|
||||||
|
away, _ := strconv.Atoi(stats[1])
|
||||||
|
return struct{ Home, Away int }{Home: home, Away: away}
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateOutcome determines the outcome status based on market type and odd
|
||||||
|
func (s *Service) evaluateOutcome(outcome domain.BetOutcome, finalScore, firstHalfScore struct{ Home, Away int }, corners struct{ Home, Away int }, events []map[string]string) (domain.OutcomeStatus, error) {
|
||||||
|
marketConfig := supportedMarkets["football"]
|
||||||
|
if !marketConfig.MarketTypes[outcome.MarketName] {
|
||||||
|
s.logger.Warn("Unsupported market type", "market_name", outcome.MarketName)
|
||||||
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch outcome.MarketName {
|
||||||
|
case "full_time_result":
|
||||||
|
return evaluateFullTimeResult(outcome, finalScore)
|
||||||
|
case "goals_over_under":
|
||||||
|
return evaluateGoalsOverUnder(outcome, finalScore)
|
||||||
|
case "correct_score":
|
||||||
|
return evaluateCorrectScore(outcome, finalScore)
|
||||||
|
case "half_time_result":
|
||||||
|
return evaluateHalfTimeResult(outcome, firstHalfScore)
|
||||||
|
case "asian_handicap":
|
||||||
|
return evaluateAsianHandicap(outcome, finalScore)
|
||||||
|
case "goal_line":
|
||||||
|
return evaluateGoalLine(outcome, finalScore)
|
||||||
|
case "1st_half_asian_handicap":
|
||||||
|
return evaluateAsianHandicap(outcome, firstHalfScore)
|
||||||
|
case "1st_half_goal_line":
|
||||||
|
return evaluateGoalLine(outcome, firstHalfScore)
|
||||||
|
case "first_team_to_score":
|
||||||
|
return evaluateFirstTeamToScore(outcome, events)
|
||||||
|
case "goals_odd_even":
|
||||||
|
return evaluateGoalsOddEven(outcome, finalScore)
|
||||||
|
case "double_chance":
|
||||||
|
return evaluateDoubleChance(outcome, finalScore)
|
||||||
|
case "draw_no_bet":
|
||||||
|
return evaluateDrawNoBet(outcome, finalScore)
|
||||||
|
default:
|
||||||
|
s.logger.Warn("Market type not implemented", "market_name", outcome.MarketName)
|
||||||
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("market type not implemented: %s", outcome.MarketName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateFullTimeResult(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "1": // Home win
|
||||||
|
if score.Home > score.Away {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
case "Draw":
|
||||||
|
if score.Home == score.Away {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
case "2": // Away win
|
||||||
|
if score.Away > score.Home {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateGoalsOverUnder(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
totalGoals := float64(score.Home + score.Away)
|
||||||
|
threshold, err := strconv.ParseFloat(outcome.OddName, 64)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if outcome.OddHeader == "Over" {
|
||||||
|
if totalGoals > threshold {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
} else if totalGoals == threshold {
|
||||||
|
return domain.OUTCOME_STATUS_VOID, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
} else if outcome.OddHeader == "Under" {
|
||||||
|
if totalGoals < threshold {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
} else if totalGoals == threshold {
|
||||||
|
return domain.OUTCOME_STATUS_VOID, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateCorrectScore(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
expectedScore := fmt.Sprintf("%d-%d", score.Home, score.Away)
|
||||||
|
if outcome.OddName == expectedScore {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateHalfTimeResult(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
return evaluateFullTimeResult(outcome, score)
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateAsianHandicap(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
handicap, err := strconv.ParseFloat(outcome.OddHandicap, 64)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid handicap: %s", outcome.OddHandicap)
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustedHomeScore := float64(score.Home)
|
||||||
|
adjustedAwayScore := float64(score.Away)
|
||||||
|
|
||||||
|
if outcome.OddHeader == "1" { // Home team
|
||||||
|
adjustedHomeScore += handicap
|
||||||
|
} else if outcome.OddHeader == "2" { // Away team
|
||||||
|
adjustedAwayScore += handicap
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
if adjustedHomeScore > adjustedAwayScore {
|
||||||
|
if outcome.OddHeader == "1" {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
} else if adjustedHomeScore < adjustedAwayScore {
|
||||||
|
if outcome.OddHeader == "2" {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_VOID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateGoalLine(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
return evaluateGoalsOverUnder(outcome, score)
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateFirstTeamToScore(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) {
|
||||||
|
for _, event := range events {
|
||||||
|
if strings.Contains(event["text"], "1st Goal") {
|
||||||
|
if strings.Contains(event["text"], outcome.HomeTeamName) && outcome.OddName == "1" {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
} else if strings.Contains(event["text"], outcome.AwayTeamName) && outcome.OddName == "2" {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_VOID, nil // No goals scored
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateGoalsOddEven(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
totalGoals := score.Home + score.Away
|
||||||
|
isOdd := totalGoals%2 == 1
|
||||||
|
if outcome.OddName == "Odd" && isOdd {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
} else if outcome.OddName == "Even" && !isOdd {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateDoubleChance(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
isHomeWin := score.Home > score.Away
|
||||||
|
isDraw := score.Home == score.Away
|
||||||
|
isAwayWin := score.Away > score.Home
|
||||||
|
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "1 or Draw":
|
||||||
|
if isHomeWin || isDraw {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
case "Draw or 2":
|
||||||
|
if isDraw || isAwayWin {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
case "1 or 2":
|
||||||
|
if isHomeWin || isAwayWin {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateDrawNoBet(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
if score.Home == score.Away {
|
||||||
|
return domain.OUTCOME_STATUS_VOID, nil
|
||||||
|
}
|
||||||
|
if outcome.OddName == "1" && score.Home > score.Away {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
} else if outcome.OddName == "2" && score.Away > score.Home {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
}
|
}
|
||||||
|
|
@ -6,11 +6,12 @@ import (
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
|
||||||
referralservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/referal"
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
|
referralservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/referal"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/result"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
|
||||||
"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"
|
||||||
|
|
@ -45,6 +46,7 @@ type App struct {
|
||||||
Logger *slog.Logger
|
Logger *slog.Logger
|
||||||
prematchSvc *odds.ServiceImpl
|
prematchSvc *odds.ServiceImpl
|
||||||
eventSvc event.Service
|
eventSvc event.Service
|
||||||
|
resultSvc *result.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApp(
|
func NewApp(
|
||||||
|
|
@ -64,6 +66,7 @@ func NewApp(
|
||||||
eventSvc event.Service,
|
eventSvc event.Service,
|
||||||
referralSvc referralservice.ReferralStore,
|
referralSvc referralservice.ReferralStore,
|
||||||
virtualGameSvc virtualgameservice.VirtualGameService,
|
virtualGameSvc virtualgameservice.VirtualGameService,
|
||||||
|
resultSvc *result.Service,
|
||||||
) *App {
|
) *App {
|
||||||
app := fiber.New(fiber.Config{
|
app := fiber.New(fiber.Config{
|
||||||
CaseSensitive: true,
|
CaseSensitive: true,
|
||||||
|
|
@ -99,6 +102,7 @@ func NewApp(
|
||||||
prematchSvc: prematchSvc,
|
prematchSvc: prematchSvc,
|
||||||
eventSvc: eventSvc,
|
eventSvc: eventSvc,
|
||||||
virtualGameSvc: virtualGameSvc,
|
virtualGameSvc: virtualGameSvc,
|
||||||
|
resultSvc: resultSvc,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.initAppRoutes()
|
s.initAppRoutes()
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.Service, resultService resultsvc.Service) {
|
func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.Service, resultService *resultsvc.Service) {
|
||||||
c := cron.New(cron.WithSeconds())
|
c := cron.New(cron.WithSeconds())
|
||||||
|
|
||||||
schedule := []struct {
|
schedule := []struct {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user