diff --git a/cmd/main.go b/cmd/main.go index 3f0972b..92f0fb6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -59,6 +59,7 @@ import ( virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/atlas" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet/monitor" @@ -153,6 +154,12 @@ func main() { aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger) veliCLient := veli.NewClient(cfg, walletSvc) veliVirtualGameService := veli.New(virtualGameSvc, vitualGameRepo, veliCLient, walletSvc, wallet.TransferStore(store), domain.MongoDBLogger, cfg) + orchestrationSvc := orchestration.New( + virtualGameSvc, + virtuaGamesRepo, + cfg, + veliCLient, + ) atlasClient := atlas.NewClient(cfg, walletSvc) atlasVirtualGameService := atlas.New(virtualGameSvc, vitualGameRepo, atlasClient, walletSvc, wallet.TransferStore(store), cfg) recommendationSvc := recommendation.NewService(recommendationRepo) @@ -194,7 +201,7 @@ func main() { ) go httpserver.StartEnetPulseCron(enePulseSvc, domain.MongoDBLogger) - go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, veliVirtualGameService, "C:/Users/User/Desktop") + go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, orchestrationSvc, "C:/Users/User/Desktop") go httpserver.ProcessBetCashback(context.TODO(), betSvc) bankRepository := repository.NewBankRepository(store) @@ -259,6 +266,7 @@ func main() { enetPulseSvc, atlasVirtualGameService, veliVirtualGameService, + orchestrationSvc, telebirrSvc, arifpaySvc, santimpaySvc, diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index 4b636ec..e791639 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -36,6 +36,24 @@ CREATE TABLE IF NOT EXISTS virtual_game_providers ( created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ ); + +CREATE TABLE IF NOT EXISTS virtual_game_provider_reports ( + id BIGSERIAL PRIMARY KEY, + provider_id VARCHAR(100) NOT NULL REFERENCES virtual_game_providers(provider_id) ON DELETE CASCADE, + report_date DATE NOT NULL, + total_games_played BIGINT DEFAULT 0, + total_bets NUMERIC(18,2) DEFAULT 0, + total_payouts NUMERIC(18,2) DEFAULT 0, + total_profit NUMERIC(18,2) GENERATED ALWAYS AS (total_bets - total_payouts) STORED, + total_players BIGINT DEFAULT 0, + report_type VARCHAR(50) DEFAULT 'daily', + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_unique_provider_report +ON virtual_game_provider_reports (provider_id, report_date, report_type); + CREATE TABLE IF NOT EXISTS virtual_games ( id BIGSERIAL PRIMARY KEY, game_id VARCHAR(150) NOT NULL, @@ -54,6 +72,25 @@ CREATE TABLE IF NOT EXISTS virtual_games ( updated_at TIMESTAMPTZ ); CREATE UNIQUE INDEX IF NOT EXISTS ux_virtual_games_provider_game ON virtual_games (provider_id, game_id); + +CREATE TABLE IF NOT EXISTS virtual_game_reports ( + id BIGSERIAL PRIMARY KEY, + game_id VARCHAR(150) NOT NULL REFERENCES virtual_games(game_id) ON DELETE CASCADE, + provider_id VARCHAR(100) NOT NULL REFERENCES virtual_game_providers(provider_id) ON DELETE CASCADE, + report_date DATE NOT NULL, + total_rounds BIGINT DEFAULT 0, + total_bets NUMERIC(18,2) DEFAULT 0, + total_payouts NUMERIC(18,2) DEFAULT 0, + total_profit NUMERIC(18,2) GENERATED ALWAYS AS (total_bets - total_payouts) STORED, + total_players BIGINT DEFAULT 0, + report_type VARCHAR(50) DEFAULT 'daily', + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_unique_game_report +ON virtual_game_reports (game_id, report_date, report_type); + CREATE TABLE IF NOT EXISTS wallets ( id BIGSERIAL PRIMARY KEY, balance BIGINT NOT NULL DEFAULT 0, diff --git a/db/query/virtual_games.sql b/db/query/virtual_games.sql index 46e5061..8bee846 100644 --- a/db/query/virtual_games.sql +++ b/db/query/virtual_games.sql @@ -287,4 +287,87 @@ WHERE ( ORDER BY vg.created_at DESC LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); -- name: DeleteAllVirtualGames :exec -DELETE FROM virtual_games; \ No newline at end of file +DELETE FROM virtual_games; + + +-- name: CreateVirtualGameProviderReport :one +INSERT INTO virtual_game_provider_reports ( + provider_id, + report_date, + total_games_played, + total_bets, + total_payouts, + total_players, + report_type, + created_at, + updated_at +) VALUES ( + $1, $2, $3, $4, $5, $6, COALESCE($7, 'daily'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP +) +ON CONFLICT (provider_id, report_date, report_type) DO UPDATE +SET + total_games_played = EXCLUDED.total_games_played, + total_bets = EXCLUDED.total_bets, + total_payouts = EXCLUDED.total_payouts, + total_players = EXCLUDED.total_players, + updated_at = CURRENT_TIMESTAMP +RETURNING *; + + +-- name: CreateVirtualGameReport :one +INSERT INTO virtual_game_reports ( + game_id, + provider_id, + report_date, + total_rounds, + total_bets, + total_payouts, + total_players, + report_type, + created_at, + updated_at +) VALUES ( + $1, $2, $3, $4, $5, $6, $7, COALESCE($8, 'daily'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP +) +ON CONFLICT (game_id, report_date, report_type) DO UPDATE +SET + total_rounds = EXCLUDED.total_rounds, + total_bets = EXCLUDED.total_bets, + total_payouts = EXCLUDED.total_payouts, + total_players = EXCLUDED.total_players, + updated_at = CURRENT_TIMESTAMP +RETURNING *; + +-- name: GetVirtualGameProviderReportByProviderAndDate :one +SELECT * +FROM virtual_game_provider_reports +WHERE provider_id = $1 + AND report_date = $2 + AND report_type = $3; + +-- name: UpdateVirtualGameProviderReportByDate :exec +UPDATE virtual_game_provider_reports +SET + total_games_played = total_games_played + $4, + total_bets = total_bets + $5, + total_payouts = total_payouts + $6, + total_players = total_players + $7, + updated_at = CURRENT_TIMESTAMP +WHERE + provider_id = $1 + AND report_date = $2 + AND report_type = $3; + +-- name: ListVirtualGameProviderReportsByGamesPlayedAsc :many +SELECT * +FROM virtual_game_provider_reports +ORDER BY total_games_played ASC; + +-- name: ListVirtualGameProviderReportsByGamesPlayedDesc :many +SELECT * +FROM virtual_game_provider_reports +ORDER BY total_games_played DESC; + + + + diff --git a/docs/docs.go b/docs/docs.go index a057097..5eddf6c 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -4627,6 +4627,58 @@ const docTemplate = `{ } } }, + "/api/v1/orchestrator/virtual-game/provider-reports/asc": { + "get": { + "description": "Retrieves all virtual game provider reports sorted by total_games_played in ascending order", + "tags": [ + "VirtualGames - Orchestration" + ], + "summary": "List virtual game provider reports (ascending)", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.VirtualGameProviderReport" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, + "/api/v1/orchestrator/virtual-game/provider-reports/desc": { + "get": { + "description": "Retrieves all virtual game provider reports sorted by total_games_played in descending order", + "tags": [ + "VirtualGames - Orchestration" + ], + "summary": "List virtual game provider reports (descending)", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.VirtualGameProviderReport" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, "/api/v1/orchestrator/virtual-games": { "get": { "description": "Returns all virtual games with optional filters (category, search, pagination)", @@ -13452,6 +13504,44 @@ const docTemplate = `{ } } }, + "domain.VirtualGameProviderReport": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "provider_id": { + "type": "string" + }, + "report_date": { + "type": "string" + }, + "report_type": { + "type": "string" + }, + "total_bets": { + "type": "number" + }, + "total_games_played": { + "type": "integer" + }, + "total_payouts": { + "type": "number" + }, + "total_players": { + "type": "integer" + }, + "total_profit": { + "type": "number" + }, + "updated_at": { + "type": "string" + } + } + }, "domain.WebhookRequest": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 46c389a..3823958 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -4619,6 +4619,58 @@ } } }, + "/api/v1/orchestrator/virtual-game/provider-reports/asc": { + "get": { + "description": "Retrieves all virtual game provider reports sorted by total_games_played in ascending order", + "tags": [ + "VirtualGames - Orchestration" + ], + "summary": "List virtual game provider reports (ascending)", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.VirtualGameProviderReport" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, + "/api/v1/orchestrator/virtual-game/provider-reports/desc": { + "get": { + "description": "Retrieves all virtual game provider reports sorted by total_games_played in descending order", + "tags": [ + "VirtualGames - Orchestration" + ], + "summary": "List virtual game provider reports (descending)", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.VirtualGameProviderReport" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, "/api/v1/orchestrator/virtual-games": { "get": { "description": "Returns all virtual games with optional filters (category, search, pagination)", @@ -13444,6 +13496,44 @@ } } }, + "domain.VirtualGameProviderReport": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "provider_id": { + "type": "string" + }, + "report_date": { + "type": "string" + }, + "report_type": { + "type": "string" + }, + "total_bets": { + "type": "number" + }, + "total_games_played": { + "type": "integer" + }, + "total_payouts": { + "type": "number" + }, + "total_players": { + "type": "integer" + }, + "total_profit": { + "type": "number" + }, + "updated_at": { + "type": "string" + } + } + }, "domain.WebhookRequest": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index db66f08..66ec732 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -2431,6 +2431,31 @@ definitions: updated_at: type: string type: object + domain.VirtualGameProviderReport: + properties: + created_at: + type: string + id: + type: integer + provider_id: + type: string + report_date: + type: string + report_type: + type: string + total_bets: + type: number + total_games_played: + type: integer + total_payouts: + type: number + total_players: + type: integer + total_profit: + type: number + updated_at: + type: string + type: object domain.WebhookRequest: properties: nonce: @@ -7167,6 +7192,42 @@ paths: summary: Create a operation tags: - branch + /api/v1/orchestrator/virtual-game/provider-reports/asc: + get: + description: Retrieves all virtual game provider reports sorted by total_games_played + in ascending order + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/domain.VirtualGameProviderReport' + type: array + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.ErrorResponse' + summary: List virtual game provider reports (ascending) + tags: + - VirtualGames - Orchestration + /api/v1/orchestrator/virtual-game/provider-reports/desc: + get: + description: Retrieves all virtual game provider reports sorted by total_games_played + in descending order + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/domain.VirtualGameProviderReport' + type: array + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.ErrorResponse' + summary: List virtual game provider reports (descending) + tags: + - VirtualGames - Orchestration /api/v1/orchestrator/virtual-games: get: consumes: diff --git a/gen/db/models.go b/gen/db/models.go index 65348ad..a3b349a 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -1063,6 +1063,35 @@ type VirtualGameProvider struct { UpdatedAt pgtype.Timestamptz `json:"updated_at"` } +type VirtualGameProviderReport struct { + ID int64 `json:"id"` + ProviderID string `json:"provider_id"` + ReportDate pgtype.Date `json:"report_date"` + TotalGamesPlayed pgtype.Int8 `json:"total_games_played"` + TotalBets pgtype.Numeric `json:"total_bets"` + TotalPayouts pgtype.Numeric `json:"total_payouts"` + TotalProfit pgtype.Numeric `json:"total_profit"` + TotalPlayers pgtype.Int8 `json:"total_players"` + ReportType pgtype.Text `json:"report_type"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +type VirtualGameReport struct { + ID int64 `json:"id"` + GameID string `json:"game_id"` + ProviderID string `json:"provider_id"` + ReportDate pgtype.Date `json:"report_date"` + TotalRounds pgtype.Int8 `json:"total_rounds"` + TotalBets pgtype.Numeric `json:"total_bets"` + TotalPayouts pgtype.Numeric `json:"total_payouts"` + TotalProfit pgtype.Numeric `json:"total_profit"` + TotalPlayers pgtype.Int8 `json:"total_players"` + ReportType pgtype.Text `json:"report_type"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + type VirtualGameSession struct { ID int64 `json:"id"` UserID int64 `json:"user_id"` diff --git a/gen/db/virtual_games.sql.go b/gen/db/virtual_games.sql.go index 5a2809a..a9e8bec 100644 --- a/gen/db/virtual_games.sql.go +++ b/gen/db/virtual_games.sql.go @@ -281,6 +281,132 @@ func (q *Queries) CreateVirtualGameProvider(ctx context.Context, arg CreateVirtu return i, err } +const CreateVirtualGameProviderReport = `-- name: CreateVirtualGameProviderReport :one +INSERT INTO virtual_game_provider_reports ( + provider_id, + report_date, + total_games_played, + total_bets, + total_payouts, + total_players, + report_type, + created_at, + updated_at +) VALUES ( + $1, $2, $3, $4, $5, $6, COALESCE($7, 'daily'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP +) +ON CONFLICT (provider_id, report_date, report_type) DO UPDATE +SET + total_games_played = EXCLUDED.total_games_played, + total_bets = EXCLUDED.total_bets, + total_payouts = EXCLUDED.total_payouts, + total_players = EXCLUDED.total_players, + updated_at = CURRENT_TIMESTAMP +RETURNING id, provider_id, report_date, total_games_played, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at +` + +type CreateVirtualGameProviderReportParams struct { + ProviderID string `json:"provider_id"` + ReportDate pgtype.Date `json:"report_date"` + TotalGamesPlayed pgtype.Int8 `json:"total_games_played"` + TotalBets pgtype.Numeric `json:"total_bets"` + TotalPayouts pgtype.Numeric `json:"total_payouts"` + TotalPlayers pgtype.Int8 `json:"total_players"` + Column7 interface{} `json:"column_7"` +} + +func (q *Queries) CreateVirtualGameProviderReport(ctx context.Context, arg CreateVirtualGameProviderReportParams) (VirtualGameProviderReport, error) { + row := q.db.QueryRow(ctx, CreateVirtualGameProviderReport, + arg.ProviderID, + arg.ReportDate, + arg.TotalGamesPlayed, + arg.TotalBets, + arg.TotalPayouts, + arg.TotalPlayers, + arg.Column7, + ) + var i VirtualGameProviderReport + err := row.Scan( + &i.ID, + &i.ProviderID, + &i.ReportDate, + &i.TotalGamesPlayed, + &i.TotalBets, + &i.TotalPayouts, + &i.TotalProfit, + &i.TotalPlayers, + &i.ReportType, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const CreateVirtualGameReport = `-- name: CreateVirtualGameReport :one +INSERT INTO virtual_game_reports ( + game_id, + provider_id, + report_date, + total_rounds, + total_bets, + total_payouts, + total_players, + report_type, + created_at, + updated_at +) VALUES ( + $1, $2, $3, $4, $5, $6, $7, COALESCE($8, 'daily'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP +) +ON CONFLICT (game_id, report_date, report_type) DO UPDATE +SET + total_rounds = EXCLUDED.total_rounds, + total_bets = EXCLUDED.total_bets, + total_payouts = EXCLUDED.total_payouts, + total_players = EXCLUDED.total_players, + updated_at = CURRENT_TIMESTAMP +RETURNING id, game_id, provider_id, report_date, total_rounds, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at +` + +type CreateVirtualGameReportParams struct { + GameID string `json:"game_id"` + ProviderID string `json:"provider_id"` + ReportDate pgtype.Date `json:"report_date"` + TotalRounds pgtype.Int8 `json:"total_rounds"` + TotalBets pgtype.Numeric `json:"total_bets"` + TotalPayouts pgtype.Numeric `json:"total_payouts"` + TotalPlayers pgtype.Int8 `json:"total_players"` + Column8 interface{} `json:"column_8"` +} + +func (q *Queries) CreateVirtualGameReport(ctx context.Context, arg CreateVirtualGameReportParams) (VirtualGameReport, error) { + row := q.db.QueryRow(ctx, CreateVirtualGameReport, + arg.GameID, + arg.ProviderID, + arg.ReportDate, + arg.TotalRounds, + arg.TotalBets, + arg.TotalPayouts, + arg.TotalPlayers, + arg.Column8, + ) + var i VirtualGameReport + err := row.Scan( + &i.ID, + &i.GameID, + &i.ProviderID, + &i.ReportDate, + &i.TotalRounds, + &i.TotalBets, + &i.TotalPayouts, + &i.TotalProfit, + &i.TotalPlayers, + &i.ReportType, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + const CreateVirtualGameSession = `-- name: CreateVirtualGameSession :one INSERT INTO virtual_game_sessions ( user_id, @@ -587,6 +713,39 @@ func (q *Queries) GetVirtualGameProviderByID(ctx context.Context, providerID str return i, err } +const GetVirtualGameProviderReportByProviderAndDate = `-- name: GetVirtualGameProviderReportByProviderAndDate :one +SELECT id, provider_id, report_date, total_games_played, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at +FROM virtual_game_provider_reports +WHERE provider_id = $1 + AND report_date = $2 + AND report_type = $3 +` + +type GetVirtualGameProviderReportByProviderAndDateParams struct { + ProviderID string `json:"provider_id"` + ReportDate pgtype.Date `json:"report_date"` + ReportType pgtype.Text `json:"report_type"` +} + +func (q *Queries) GetVirtualGameProviderReportByProviderAndDate(ctx context.Context, arg GetVirtualGameProviderReportByProviderAndDateParams) (VirtualGameProviderReport, error) { + row := q.db.QueryRow(ctx, GetVirtualGameProviderReportByProviderAndDate, arg.ProviderID, arg.ReportDate, arg.ReportType) + var i VirtualGameProviderReport + err := row.Scan( + &i.ID, + &i.ProviderID, + &i.ReportDate, + &i.TotalGamesPlayed, + &i.TotalBets, + &i.TotalPayouts, + &i.TotalProfit, + &i.TotalPlayers, + &i.ReportType, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + const GetVirtualGameSessionByToken = `-- name: GetVirtualGameSessionByToken :one SELECT id, user_id, @@ -745,6 +904,82 @@ func (q *Queries) ListFavoriteGames(ctx context.Context, userID int64) ([]int64, return items, nil } +const ListVirtualGameProviderReportsByGamesPlayedAsc = `-- name: ListVirtualGameProviderReportsByGamesPlayedAsc :many +SELECT id, provider_id, report_date, total_games_played, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at +FROM virtual_game_provider_reports +ORDER BY total_games_played ASC +` + +func (q *Queries) ListVirtualGameProviderReportsByGamesPlayedAsc(ctx context.Context) ([]VirtualGameProviderReport, error) { + rows, err := q.db.Query(ctx, ListVirtualGameProviderReportsByGamesPlayedAsc) + if err != nil { + return nil, err + } + defer rows.Close() + var items []VirtualGameProviderReport + for rows.Next() { + var i VirtualGameProviderReport + if err := rows.Scan( + &i.ID, + &i.ProviderID, + &i.ReportDate, + &i.TotalGamesPlayed, + &i.TotalBets, + &i.TotalPayouts, + &i.TotalProfit, + &i.TotalPlayers, + &i.ReportType, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const ListVirtualGameProviderReportsByGamesPlayedDesc = `-- name: ListVirtualGameProviderReportsByGamesPlayedDesc :many +SELECT id, provider_id, report_date, total_games_played, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at +FROM virtual_game_provider_reports +ORDER BY total_games_played DESC +` + +func (q *Queries) ListVirtualGameProviderReportsByGamesPlayedDesc(ctx context.Context) ([]VirtualGameProviderReport, error) { + rows, err := q.db.Query(ctx, ListVirtualGameProviderReportsByGamesPlayedDesc) + if err != nil { + return nil, err + } + defer rows.Close() + var items []VirtualGameProviderReport + for rows.Next() { + var i VirtualGameProviderReport + if err := rows.Scan( + &i.ID, + &i.ProviderID, + &i.ReportDate, + &i.TotalGamesPlayed, + &i.TotalBets, + &i.TotalPayouts, + &i.TotalProfit, + &i.TotalPlayers, + &i.ReportType, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const ListVirtualGameProviders = `-- name: ListVirtualGameProviders :many SELECT id, provider_id, @@ -845,6 +1080,43 @@ func (q *Queries) UpdateVirtualGameProviderEnabled(ctx context.Context, arg Upda return i, err } +const UpdateVirtualGameProviderReportByDate = `-- name: UpdateVirtualGameProviderReportByDate :exec +UPDATE virtual_game_provider_reports +SET + total_games_played = total_games_played + $4, + total_bets = total_bets + $5, + total_payouts = total_payouts + $6, + total_players = total_players + $7, + updated_at = CURRENT_TIMESTAMP +WHERE + provider_id = $1 + AND report_date = $2 + AND report_type = $3 +` + +type UpdateVirtualGameProviderReportByDateParams struct { + ProviderID string `json:"provider_id"` + ReportDate pgtype.Date `json:"report_date"` + ReportType pgtype.Text `json:"report_type"` + TotalGamesPlayed pgtype.Int8 `json:"total_games_played"` + TotalBets pgtype.Numeric `json:"total_bets"` + TotalPayouts pgtype.Numeric `json:"total_payouts"` + TotalPlayers pgtype.Int8 `json:"total_players"` +} + +func (q *Queries) UpdateVirtualGameProviderReportByDate(ctx context.Context, arg UpdateVirtualGameProviderReportByDateParams) error { + _, err := q.db.Exec(ctx, UpdateVirtualGameProviderReportByDate, + arg.ProviderID, + arg.ReportDate, + arg.ReportType, + arg.TotalGamesPlayed, + arg.TotalBets, + arg.TotalPayouts, + arg.TotalPlayers, + ) + return err +} + const UpdateVirtualGameSessionStatus = `-- name: UpdateVirtualGameSessionStatus :exec UPDATE virtual_game_sessions SET status = $2, diff --git a/internal/domain/virtual_game.go b/internal/domain/virtual_game.go index 9929c5d..0a8bcbd 100644 --- a/internal/domain/virtual_game.go +++ b/internal/domain/virtual_game.go @@ -316,3 +316,61 @@ type UnifiedGame struct { Status int `json:"status,omitempty"` DemoURL string `json:"demoUrl"` } + +type CreateVirtualGameProviderReport struct { + ProviderID string `json:"provider_id"` + ReportDate time.Time `json:"report_date"` + TotalGamesPlayed int64 `json:"total_games_played"` + TotalBets float64 `json:"total_bets"` + TotalPayouts float64 `json:"total_payouts"` + TotalPlayers int64 `json:"total_players"` + ReportType string `json:"report_type"` // e.g., "daily", "weekly" +} + +type VirtualGameProviderReport struct { + ID int64 `json:"id"` + ProviderID string `json:"provider_id"` + ReportDate time.Time `json:"report_date"` + TotalGamesPlayed int64 `json:"total_games_played"` + TotalBets float64 `json:"total_bets"` + TotalPayouts float64 `json:"total_payouts"` + TotalProfit float64 `json:"total_profit"` + TotalPlayers int64 `json:"total_players"` + ReportType string `json:"report_type"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type CreateVirtualGameReport struct { + GameID string `json:"game_id"` + ProviderID string `json:"provider_id"` + ReportDate time.Time `json:"report_date"` + TotalRounds int64 `json:"total_rounds"` + TotalBets float64 `json:"total_bets"` + TotalPayouts float64 `json:"total_payouts"` + TotalPlayers int64 `json:"total_players"` + ReportType string `json:"report_type"` // e.g., "daily", "weekly" +} + +type VirtualGameReport struct { + ID int64 `json:"id"` + GameID string `json:"game_id"` + ProviderID string `json:"provider_id"` + ReportDate time.Time `json:"report_date"` + TotalRounds int64 `json:"total_rounds"` + TotalBets float64 `json:"total_bets"` + TotalPayouts float64 `json:"total_payouts"` + TotalProfit float64 `json:"total_profit"` + TotalPlayers int64 `json:"total_players"` + ReportType string `json:"report_type"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type CreateVirtualGameProviderReportsRequest struct { + Reports []CreateVirtualGameProviderReport +} + +type CreateVirtualGameReportsRequest struct { + Reports []CreateVirtualGameReport +} diff --git a/internal/repository/virtual_game.go b/internal/repository/virtual_game.go index c792801..f970a9d 100644 --- a/internal/repository/virtual_game.go +++ b/internal/repository/virtual_game.go @@ -4,6 +4,8 @@ import ( "context" "database/sql" "errors" + "fmt" + "time" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" @@ -36,6 +38,12 @@ type VirtualGameRepository interface { CreateVirtualGame(ctx context.Context, arg dbgen.CreateVirtualGameParams) (dbgen.VirtualGame, error) ListAllVirtualGames(ctx context.Context, arg dbgen.GetAllVirtualGamesParams) ([]dbgen.GetAllVirtualGamesRow, error) RemoveAllVirtualGames(ctx context.Context) error + CreateVirtualGameProviderReport(ctx context.Context, report domain.CreateVirtualGameProviderReport) (domain.VirtualGameProviderReport, error) + CreateVirtualGameReport(ctx context.Context, report domain.CreateVirtualGameReport) (domain.VirtualGameReport, error) + GetVirtualGameProviderReportByProviderAndDate(ctx context.Context, providerID string, createdAt time.Time, reportType string) (domain.VirtualGameProviderReport, error) + UpdateVirtualGameProviderReportByDate(ctx context.Context, providerID string, reportDate time.Time, reportType string, totalGamesPlayed int64, totalBets float64, totalPayouts float64, totalPlayers int64) error + ListVirtualGameProviderReportsByGamesPlayedAsc(ctx context.Context) ([]domain.VirtualGameProviderReport, error) + ListVirtualGameProviderReportsByGamesPlayedDesc(ctx context.Context) ([]domain.VirtualGameProviderReport, error) } type VirtualGameRepo struct { @@ -314,3 +322,183 @@ func (r *VirtualGameRepo) ListAllVirtualGames(ctx context.Context, arg dbgen.Get func (r *VirtualGameRepo) RemoveAllVirtualGames(ctx context.Context) error { return r.store.queries.DeleteAllVirtualGames(ctx) } + +func (r *VirtualGameRepo) CreateVirtualGameProviderReport( + ctx context.Context, + report domain.CreateVirtualGameProviderReport, +) (domain.VirtualGameProviderReport, error) { + dbReport, err := r.store.queries.CreateVirtualGameProviderReport( + ctx, + ConvertCreateVirtualGameProviderReport(report), + ) + if err != nil { + return domain.VirtualGameProviderReport{}, err + } + + return ConvertDBVirtualGameProviderReport(dbReport), nil +} + +func (r *VirtualGameRepo) CreateVirtualGameReport( + ctx context.Context, + report domain.CreateVirtualGameReport, +) (domain.VirtualGameReport, error) { + dbReport, err := r.store.queries.CreateVirtualGameReport( + ctx, + ConvertCreateVirtualGameReport(report), + ) + if err != nil { + return domain.VirtualGameReport{}, err + } + + return ConvertDBVirtualGameReport(dbReport), nil +} + +func (r *VirtualGameRepo) GetVirtualGameProviderReportByProviderAndDate( + ctx context.Context, + providerID string, + reportDate time.Time, + reportType string, +) (domain.VirtualGameProviderReport, error) { + arg := dbgen.GetVirtualGameProviderReportByProviderAndDateParams{ + ProviderID: providerID, + ReportDate: pgtype.Date{Time: reportDate, Valid: true}, + ReportType: pgtype.Text{String: reportType, Valid: true}, + } + + dbReport, err := r.store.queries.GetVirtualGameProviderReportByProviderAndDate(ctx, arg) + if err != nil { + return domain.VirtualGameProviderReport{}, err + } + + return ConvertDBVirtualGameProviderReport(dbReport), nil +} + +func (r *VirtualGameRepo) UpdateVirtualGameProviderReportByDate( + ctx context.Context, + providerID string, + reportDate time.Time, + reportType string, + totalGamesPlayed int64, + totalBets float64, + totalPayouts float64, + totalPlayers int64, +) error { + arg := dbgen.UpdateVirtualGameProviderReportByDateParams{ + ProviderID: providerID, + ReportDate: pgtype.Date{Time: reportDate, Valid: true}, + ReportType: pgtype.Text{String: reportType, Valid: true}, + TotalGamesPlayed: pgtype.Int8{Int64: totalGamesPlayed, Valid: true}, + TotalBets: pgtype.Numeric{}, + TotalPayouts: pgtype.Numeric{}, + TotalPlayers: pgtype.Int8{Int64: totalPlayers, Valid: true}, + } + + // Safely convert float64 → pgtype.Numeric + if err := arg.TotalBets.Scan(totalBets); err != nil { + return fmt.Errorf("failed to set total_bets: %w", err) + } + if err := arg.TotalPayouts.Scan(totalPayouts); err != nil { + return fmt.Errorf("failed to set total_payouts: %w", err) + } + + if err := r.store.queries.UpdateVirtualGameProviderReportByDate(ctx, arg); err != nil { + return fmt.Errorf("failed to update provider report for %s: %w", providerID, err) + } + + return nil +} + +func (r *VirtualGameRepo) ListVirtualGameProviderReportsByGamesPlayedAsc( + ctx context.Context, +) ([]domain.VirtualGameProviderReport, error) { + dbReports, err := r.store.queries.ListVirtualGameProviderReportsByGamesPlayedAsc(ctx) + if err != nil { + return nil, err + } + + reports := make([]domain.VirtualGameProviderReport, len(dbReports)) + for i, r := range dbReports { + reports[i] = ConvertDBVirtualGameProviderReport(r) + } + + return reports, nil +} + +func (r *VirtualGameRepo) ListVirtualGameProviderReportsByGamesPlayedDesc( + ctx context.Context, +) ([]domain.VirtualGameProviderReport, error) { + dbReports, err := r.store.queries.ListVirtualGameProviderReportsByGamesPlayedDesc(ctx) + if err != nil { + return nil, err + } + + reports := make([]domain.VirtualGameProviderReport, len(dbReports)) + for i, r := range dbReports { + reports[i] = ConvertDBVirtualGameProviderReport(r) + } + + return reports, nil +} + +func ConvertCreateVirtualGameProviderReport(r domain.CreateVirtualGameProviderReport) dbgen.CreateVirtualGameProviderReportParams { + // var totalBets, totalPayouts pgtype.Numeric + + // _ = r.TotalBets. + // _ = totalPayouts.Set(r.TotalPayouts) + + return dbgen.CreateVirtualGameProviderReportParams{ + ProviderID: r.ProviderID, + ReportDate: pgtype.Date{Time: r.ReportDate, Valid: true}, + TotalGamesPlayed: pgtype.Int8{Int64: r.TotalGamesPlayed, Valid: true}, + TotalBets: pgtype.Numeric{Exp: int32(r.TotalBets)}, + TotalPayouts: pgtype.Numeric{Exp: int32(r.TotalPayouts)}, + TotalPlayers: pgtype.Int8{Int64: r.TotalPlayers, Valid: true}, + Column7: pgtype.Text{String: r.ReportType, Valid: true}, + } +} + +func ConvertDBVirtualGameProviderReport(db dbgen.VirtualGameProviderReport) domain.VirtualGameProviderReport { + return domain.VirtualGameProviderReport{ + ID: db.ID, + ProviderID: db.ProviderID, + ReportDate: db.ReportDate.Time, + TotalGamesPlayed: db.TotalGamesPlayed.Int64, + TotalBets: float64(db.TotalBets.Exp), + TotalPayouts: float64(db.TotalPayouts.Exp), + TotalProfit: float64(db.TotalProfit.Exp), + TotalPlayers: db.TotalPlayers.Int64, + ReportType: db.ReportType.String, + CreatedAt: db.CreatedAt.Time, + UpdatedAt: db.UpdatedAt.Time, + } +} + +func ConvertCreateVirtualGameReport(r domain.CreateVirtualGameReport) dbgen.CreateVirtualGameReportParams { + return dbgen.CreateVirtualGameReportParams{ + GameID: r.GameID, + ProviderID: r.ProviderID, + ReportDate: pgtype.Date{Time: r.ReportDate}, + TotalRounds: pgtype.Int8{Int64: r.TotalRounds}, + TotalBets: pgtype.Numeric{Exp: int32(r.TotalBets)}, + TotalPayouts: pgtype.Numeric{Exp: int32(r.TotalPayouts)}, + TotalPlayers: pgtype.Int8{Int64: r.TotalPlayers}, + Column8: r.ReportType, + } +} + +func ConvertDBVirtualGameReport(db dbgen.VirtualGameReport) domain.VirtualGameReport { + return domain.VirtualGameReport{ + ID: db.ID, + GameID: db.GameID, + ProviderID: db.ProviderID, + ReportDate: db.ReportDate.Time, + TotalRounds: db.TotalRounds.Int64, + TotalBets: float64(db.TotalBets.Exp), + TotalPayouts: float64(db.TotalPayouts.Exp), + TotalProfit: float64(db.TotalProfit.Exp), + TotalPlayers: db.TotalPlayers.Int64, + ReportType: db.ReportType.String, + CreatedAt: db.CreatedAt.Time, + UpdatedAt: db.UpdatedAt.Time, + } +} diff --git a/internal/services/virtualGame/game_orchestration.go b/internal/services/virtualGame/game_orchestration.go deleted file mode 100644 index 73972b0..0000000 --- a/internal/services/virtualGame/game_orchestration.go +++ /dev/null @@ -1,81 +0,0 @@ -package virtualgameservice - -import ( - "context" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -// Remove a provider by provider_id -func (s *service) RemoveProvider(ctx context.Context, providerID string) error { - return s.repo.DeleteVirtualGameProvider(ctx, providerID) -} - -// Fetch provider by provider_id -func (s *service) GetProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) { - return s.repo.GetVirtualGameProviderByID(ctx, providerID) -} - -// List providers with pagination -func (s *service) ListProviders(ctx context.Context, limit, offset int32) ([]domain.VirtualGameProvider, int64, error) { - providers, err := s.repo.ListVirtualGameProviders(ctx, limit, offset) - if err != nil { - return nil, 0, err - } - - total, err := s.repo.CountVirtualGameProviders(ctx) - if err != nil { - return nil, 0, err - } - - // Convert []dbgen.VirtualGameProvider to []domain.VirtualGameProvider - domainProviders := make([]domain.VirtualGameProvider, len(providers)) - for i, p := range providers { - var logoDark *string - if p.LogoDark.Valid { - logoDark = &p.LogoDark.String - } - - var logoLight *string - if p.LogoLight.Valid { - logoLight = &p.LogoLight.String - } - - domainProviders[i] = domain.VirtualGameProvider{ - ProviderID: p.ProviderID, - ProviderName: p.ProviderName, - Enabled: p.Enabled, - LogoDark: logoDark, - LogoLight: logoLight, - CreatedAt: p.CreatedAt.Time, - UpdatedAt: &p.UpdatedAt.Time, - // Add other fields as needed - } - } - - return domainProviders, total, nil -} - -// Enable/Disable a provider -func (s *service) SetProviderEnabled(ctx context.Context, providerID string, enabled bool) (*domain.VirtualGameProvider, error) { - provider, err := s.repo.UpdateVirtualGameProviderEnabled(ctx, providerID, enabled) - if err != nil { - s.logger.Error("Failed to update provider enabled status", "provider_id", providerID, "enabled", enabled, "error", err) - return nil, err - } - now := time.Now() - provider.UpdatedAt.Time = now - - domainProvider := &domain.VirtualGameProvider{ - ProviderID: provider.ProviderID, - ProviderName: provider.ProviderName, - Enabled: provider.Enabled, - CreatedAt: provider.CreatedAt.Time, - UpdatedAt: &provider.UpdatedAt.Time, - // Add other fields as needed - } - - return domainProvider, nil -} diff --git a/internal/services/virtualGame/orchestration/port.go b/internal/services/virtualGame/orchestration/port.go new file mode 100644 index 0000000..d44ccd7 --- /dev/null +++ b/internal/services/virtualGame/orchestration/port.go @@ -0,0 +1,14 @@ +package orchestration + +import ( + "context" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +) + +type OrchestrationService interface { + FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) + GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error) + AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) +} diff --git a/internal/services/virtualGame/orchestration/service.go b/internal/services/virtualGame/orchestration/service.go new file mode 100644 index 0000000..d237f11 --- /dev/null +++ b/internal/services/virtualGame/orchestration/service.go @@ -0,0 +1,526 @@ +package orchestration + +import ( + "context" + "fmt" + "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/SamuelTariku/FortuneBet-Backend/internal/config" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" + virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" + "github.com/jackc/pgx/v5/pgtype" +) + +type Service struct { + virtualGameSvc virtualgameservice.VirtualGameService + veliVirtualGameSvc veli.VeliVirtualGameService + repo repository.VirtualGameRepository + cfg *config.Config + client *veli.Client +} + +func New(virtualGameSvc virtualgameservice.VirtualGameService, repo repository.VirtualGameRepository, cfg *config.Config, client *veli.Client) *Service { + return &Service{ + virtualGameSvc: virtualGameSvc, + repo: repo, + cfg: cfg, + client: client, + } +} + +func (s *Service) AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) { + + // logger := s.mongoLogger.With(zap.String("service", "AddProviders"), zap.Any("ProviderRequest", req)) + + // 0. Remove all existing providers first + if err := s.repo.DeleteAllVirtualGameProviders(ctx); err != nil { + // logger.Error("failed to delete all virtual game providers", zap.Error(err)) + return nil, fmt.Errorf("failed to clear existing providers: %w", err) + } + + // 1. Prepare signature parameters + sigParams := map[string]any{ + "brandId": req.BrandID, + } + + // Optional fields + sigParams["extraData"] = fmt.Sprintf("%t", req.ExtraData) // false is still included + if req.Size > 0 { + sigParams["size"] = fmt.Sprintf("%d", req.Size) + } else { + sigParams["size"] = "" + } + + if req.Page > 0 { + sigParams["page"] = fmt.Sprintf("%d", req.Page) + } else { + sigParams["page"] = "" + } + + // 2. Call external API + var res domain.ProviderResponse + if err := s.client.Post(ctx, "/game-lists/public/providers", req, sigParams, &res); err != nil { + return nil, fmt.Errorf("failed to fetch providers: %w", err) + } + + // 3. Loop through fetched providers and insert into DB + for _, p := range res.Items { + createParams := dbgen.CreateVirtualGameProviderParams{ + ProviderID: p.ProviderID, + ProviderName: p.ProviderName, + LogoDark: pgtype.Text{String: p.LogoForDark, Valid: p.LogoForDark != ""}, + LogoLight: pgtype.Text{String: p.LogoForLight, Valid: p.LogoForLight != ""}, + Enabled: true, + } + + if _, err := s.repo.CreateVirtualGameProvider(ctx, createParams); err != nil { + // logger.Error("failed to add provider", zap.Error(err)) + return nil, fmt.Errorf("failed to add provider %s: %w", p.ProviderID, err) + } + } + + // 4. Always add "popok" provider manually + popokParams := dbgen.CreateVirtualGameProviderParams{ + ProviderID: "popok", + ProviderName: "Popok Gaming", + LogoDark: pgtype.Text{String: fmt.Sprintf("%v/static/logos/popok-dark.png", s.cfg.PopOK.CallbackURL), Valid: true}, // adjust as needed + LogoLight: pgtype.Text{String: fmt.Sprintf("%v/static/logos/popok-light.png", s.cfg.PopOK.CallbackURL), Valid: true}, // adjust as needed + Enabled: true, + } + + atlasParams := dbgen.CreateVirtualGameProviderParams{ + ProviderID: "atlas", + ProviderName: "Atlas Gaming", + LogoDark: pgtype.Text{String: "/static/logos/atlas-dark.png", Valid: true}, // adjust as needed + LogoLight: pgtype.Text{String: "/static/logos/atlas-light.png", Valid: true}, // adjust as needed + Enabled: true, + } + + if _, err := s.repo.CreateVirtualGameProvider(ctx, popokParams); err != nil { + // logger.Error("failed to add popok provider", zap.Any("popokParams", popokParams), zap.Error(err)) + return nil, fmt.Errorf("failed to add popok provider: %w", err) + } + + if _, err := s.repo.CreateVirtualGameProvider(ctx, atlasParams); err != nil { + return nil, fmt.Errorf("failed to add atlas provider: %w", err) + } + + // Optionally also append it to the response for consistency + // res.Items = append(res.Items, domain.VirtualGameProvider{ + // ProviderID: uuid.New().String(), + // ProviderName: "Popok Gaming", + // LogoForDark: "/static/logos/popok-dark.png", + // LogoForLight: "/static/logos/popok-light.png", + // }) + + return &res, nil +} + +func (s *Service) GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error) { + // Build params for repo call + // logger := s.mongoLogger.With(zap.String("service", "GetAllVirtualGames"), zap.Any("params", params)) + rows, err := s.repo.ListAllVirtualGames(ctx, params) + if err != nil { + // logger.Error("[GetAllVirtualGames] Failed to fetch virtual games", zap.Error(err)) + return nil, fmt.Errorf("failed to fetch virtual games: %w", err) + } + + var allGames []domain.UnifiedGame + for _, r := range rows { + // --- Convert nullable Rtp to *float64 --- + var rtpPtr *float64 + if r.Rtp.Valid { + rtpFloat, err := r.Rtp.Float64Value() + if err == nil { + rtpPtr = new(float64) + *rtpPtr = rtpFloat.Float64 + } + } + var betsFloat64 []float64 + for _, bet := range r.Bets { + if bet.Valid { + betFloat, err := bet.Float64Value() + if err == nil { + betsFloat64 = append(betsFloat64, betFloat.Float64) + } + } + } + + allGames = append(allGames, domain.UnifiedGame{ + GameID: r.GameID, + ProviderID: r.ProviderID, + Provider: r.ProviderName, + Name: r.Name, + Category: r.Category.String, + DeviceType: r.DeviceType.String, + Volatility: r.Volatility.String, + RTP: rtpPtr, + HasDemo: r.HasDemo.Bool, + HasFreeBets: r.HasFreeBets.Bool, + Bets: betsFloat64, + Thumbnail: r.Thumbnail.String, + Status: int(r.Status.Int32), // nullable status + }) + } + + return allGames, nil +} + +func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) { + // logger := s.mongoLogger.With( + // zap.String("service", "FetchAndStoreAllVirtualGames"), + // zap.Any("ProviderRequest", req), + // ) + + // This is necessary since the provider is a foreign key + _, err := s.AddProviders(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to add providers to database: %w", err) + } + + var allGames []domain.UnifiedGame + + // --- 1. Existing providers (Veli Games) --- + providersRes, err := s.veliVirtualGameSvc.GetProviders(ctx, req) + if err != nil { + // logger.Error("Failed to fetch provider", zap.Error(err)) + return nil, fmt.Errorf("failed to fetch providers: %w", err) + } + + // --- 2. Fetch games for each provider (Veli Games) --- + for _, p := range providersRes.Items { + games, err := s.veliVirtualGameSvc.GetGames(ctx, domain.GameListRequest{ + BrandID: s.cfg.VeliGames.BrandID, + ProviderID: p.ProviderID, + Page: req.Page, + Size: req.Size, + }) + if err != nil { + // logger.Error("failed to get veli games", zap.String("ProviderID", p.ProviderID), zap.Error(err)) + continue // skip failing provider but continue others + } + + for _, g := range games { + unified := domain.UnifiedGame{ + GameID: g.GameID, + ProviderID: g.ProviderID, + Provider: p.ProviderName, + Name: g.Name, + Category: g.Category, + DeviceType: g.DeviceType, + HasDemo: g.HasDemoMode, + HasFreeBets: g.HasFreeBets, + } + allGames = append(allGames, unified) + + // Save to DB + _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ + GameID: g.GameID, + ProviderID: g.ProviderID, + Name: g.Name, + Category: pgtype.Text{ + String: g.Category, + Valid: g.Category != "", + }, + DeviceType: pgtype.Text{ + String: g.DeviceType, + Valid: g.DeviceType != "", + }, + HasDemo: pgtype.Bool{ + Bool: g.HasDemoMode, + Valid: true, + }, + HasFreeBets: pgtype.Bool{ + Bool: g.HasFreeBets, + Valid: true, + }, + }) + if err != nil { + // logger.Error("failed to create virtual game", zap.Error(err)) + } + } + } + + // --- 3. Fetch Atlas-V games --- + atlasGames, err := s.veliVirtualGameSvc.GetAtlasVGames(ctx) + if err != nil { + // logger.Error("failed to fetch Atlas-V games", zap.Error(err)) + } else { + for _, g := range atlasGames { + unified := domain.UnifiedGame{ + GameID: g.GameID, + ProviderID: "atlasv", + Provider: "Atlas-V Gaming", // "Atlas-V" + Name: g.Name, + Category: g.Category, // using Type as Category + Thumbnail: g.Thumbnail, + HasDemo: true, + DemoURL: g.DemoURL, + } + allGames = append(allGames, unified) + + // Save to DB + _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ + GameID: g.GameID, + ProviderID: "atlasv", + Name: g.Name, + Category: pgtype.Text{ + String: g.Category, + Valid: g.Category != "", + }, + Thumbnail: pgtype.Text{ + String: g.Thumbnail, + Valid: g.Thumbnail != "", + }, + HasDemo: pgtype.Bool{ + Bool: g.HasDemoMode, + Valid: true, + }, + }) + if err != nil { + // logger.Error("failed to create Atlas-V virtual game", zap.Error(err)) + } + } + } + + // --- 4. Handle PopOK separately --- + popokGames, err := s.virtualGameSvc.ListGames(ctx, currency) + if err != nil { + // logger.Error("failed to fetch PopOk games", zap.Error(err)) + return nil, fmt.Errorf("failed to fetch PopOK games: %w", err) + } + + for _, g := range popokGames { + unified := domain.UnifiedGame{ + GameID: fmt.Sprintf("%d", g.ID), + ProviderID: "popok", + Provider: "PopOK", + Name: g.GameName, + Category: "Crash", + Bets: g.Bets, + Thumbnail: g.Thumbnail, + Status: g.Status, + } + allGames = append(allGames, unified) + + // Convert []float64 to []pgtype.Numeric + var betsNumeric []pgtype.Numeric + for _, bet := range g.Bets { + var num pgtype.Numeric + _ = num.Scan(bet) + betsNumeric = append(betsNumeric, num) + } + + // Save to DB + _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ + GameID: fmt.Sprintf("%d", g.ID), + ProviderID: "popok", + Name: g.GameName, + Bets: betsNumeric, + Thumbnail: pgtype.Text{ + String: g.Thumbnail, + Valid: g.Thumbnail != "", + }, + Status: pgtype.Int4{ + Int32: int32(g.Status), + Valid: true, + }, + HasDemo: pgtype.Bool{ + Bool: true, + Valid: true, + }, + Category: pgtype.Text{ + String: "Crash", + Valid: true, + }, + }) + if err != nil { + // logger.Error("failed to create PopOK virtual game", zap.Error(err)) + } + } + + return allGames, nil +} + +func (s *Service) RemoveProvider(ctx context.Context, providerID string) error { + return s.repo.DeleteVirtualGameProvider(ctx, providerID) +} + +// Fetch provider by provider_id +func (s *Service) GetProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) { + return s.repo.GetVirtualGameProviderByID(ctx, providerID) +} + +// List providers with pagination +func (s *Service) ListProviders(ctx context.Context, limit, offset int32) ([]domain.VirtualGameProvider, int64, error) { + providers, err := s.repo.ListVirtualGameProviders(ctx, limit, offset) + if err != nil { + return nil, 0, err + } + + total, err := s.repo.CountVirtualGameProviders(ctx) + if err != nil { + return nil, 0, err + } + + // Convert []dbgen.VirtualGameProvider to []domain.VirtualGameProvider + domainProviders := make([]domain.VirtualGameProvider, len(providers)) + for i, p := range providers { + var logoDark *string + if p.LogoDark.Valid { + logoDark = &p.LogoDark.String + } + + var logoLight *string + if p.LogoLight.Valid { + logoLight = &p.LogoLight.String + } + + domainProviders[i] = domain.VirtualGameProvider{ + ProviderID: p.ProviderID, + ProviderName: p.ProviderName, + Enabled: p.Enabled, + LogoDark: logoDark, + LogoLight: logoLight, + CreatedAt: p.CreatedAt.Time, + UpdatedAt: &p.UpdatedAt.Time, + // Add other fields as needed + } + } + + return domainProviders, total, nil +} + +// Enable/Disable a provider +func (s *Service) SetProviderEnabled(ctx context.Context, providerID string, enabled bool) (*domain.VirtualGameProvider, error) { + provider, err := s.repo.UpdateVirtualGameProviderEnabled(ctx, providerID, enabled) + if err != nil { + //s.logger.Error("Failed to update provider enabled status", "provider_id", providerID, "enabled", enabled, "error", err) + return nil, err + } + now := time.Now() + provider.UpdatedAt.Time = now + + domainProvider := &domain.VirtualGameProvider{ + ProviderID: provider.ProviderID, + ProviderName: provider.ProviderName, + Enabled: provider.Enabled, + CreatedAt: provider.CreatedAt.Time, + UpdatedAt: &provider.UpdatedAt.Time, + // Add other fields as needed + } + + return domainProvider, nil +} + +func (s *Service) CreateVirtualGameProviderReport(ctx context.Context, report domain.CreateVirtualGameProviderReport) (domain.VirtualGameProviderReport, error) { + // Example: logger := s.mongoLogger.With(zap.String("service", "CreateVirtualGameProviderReport"), zap.Any("Report", report)) + + // Call repository to create the report + created, err := s.repo.CreateVirtualGameProviderReport(ctx, report) + if err != nil { + // logger.Error("failed to create provider report", zap.Error(err), zap.Any("report", report)) + return domain.VirtualGameProviderReport{}, fmt.Errorf("failed to create provider report for provider %s: %w", report.ProviderID, err) + } + + // Return the created report + return created, nil +} + +func (s *Service) CreateVirtualGameReport(ctx context.Context, report domain.CreateVirtualGameReport) (domain.VirtualGameReport, error) { + // Example: logger := s.mongoLogger.With(zap.String("service", "CreateVirtualGameReport"), zap.Any("Report", report)) + + // Call repository to create the report + created, err := s.repo.CreateVirtualGameReport(ctx, report) + if err != nil { + // logger.Error("failed to create game report", zap.Error(err), zap.Any("report", report)) + return domain.VirtualGameReport{}, fmt.Errorf("failed to create game report for game %s: %w", report.GameID, err) + } + + // Return the created report + return created, nil +} + +func (s *Service) GetVirtualGameProviderReportByProviderAndDate( + ctx context.Context, + providerID string, + reportDate time.Time, + reportType string, +) (domain.VirtualGameProviderReport, error) { + // Example logger if needed + // logger := s.mongoLogger.With(zap.String("service", "GetVirtualGameProviderReportByProviderAndDate"), + // zap.String("provider_id", providerID), + // zap.Time("report_date", reportDate), + // zap.String("report_type", reportType), + // ) + + report, err := s.repo.GetVirtualGameProviderReportByProviderAndDate(ctx, providerID, reportDate, reportType) + if err != nil { + // logger.Error("failed to retrieve virtual game provider report", zap.Error(err)) + return domain.VirtualGameProviderReport{}, fmt.Errorf( + "failed to retrieve provider report for provider %s on %s (%s): %w", + providerID, reportDate.Format("2006-01-02"), reportType, err, + ) + } + + return report, nil +} + +func (s *Service) UpdateVirtualGameProviderReportByDate( + ctx context.Context, + providerID string, + reportDate time.Time, + reportType string, + totalGamesPlayed int64, + totalBets float64, + totalPayouts float64, + totalPlayers int64, +) error { + // Optionally log or trace the update + // Example: s.mongoLogger.Info("Updating virtual game provider report", + // zap.String("provider_id", providerID), + // zap.Time("report_date", reportDate), + // zap.String("report_type", reportType), + // ) + + err := s.repo.UpdateVirtualGameProviderReportByDate( + ctx, + providerID, + reportDate, + reportType, + totalGamesPlayed, + totalBets, + totalPayouts, + totalPlayers, + ) + if err != nil { + return fmt.Errorf("failed to update provider report for provider %s on %s (%s): %w", + providerID, + reportDate.Format("2006-01-02"), + reportType, + err, + ) + } + + return nil +} + +func (s *Service) ListVirtualGameProviderReportsByGamesPlayedAsc(ctx context.Context) ([]domain.VirtualGameProviderReport, error) { + reports, err := s.repo.ListVirtualGameProviderReportsByGamesPlayedAsc(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch virtual game provider reports ascending: %w", err) + } + return reports, nil +} + +// ListVirtualGameProviderReportsByGamesPlayedDesc fetches all reports sorted by total_games_played descending. +func (s *Service) ListVirtualGameProviderReportsByGamesPlayedDesc(ctx context.Context) ([]domain.VirtualGameProviderReport, error) { + reports, err := s.repo.ListVirtualGameProviderReportsByGamesPlayedDesc(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch virtual game provider reports descending: %w", err) + } + return reports, nil +} diff --git a/internal/services/virtualGame/port.go b/internal/services/virtualGame/port.go index 8121ea1..035d34b 100644 --- a/internal/services/virtualGame/port.go +++ b/internal/services/virtualGame/port.go @@ -3,16 +3,15 @@ package virtualgameservice import ( "context" - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) type VirtualGameService interface { // AddProvider(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) - RemoveProvider(ctx context.Context, providerID string) error - GetProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) - ListProviders(ctx context.Context, limit, offset int32) ([]domain.VirtualGameProvider, int64, error) - SetProviderEnabled(ctx context.Context, providerID string, enabled bool) (*domain.VirtualGameProvider, error) + // RemoveProvider(ctx context.Context, providerID string) error + // GetProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) + // ListProviders(ctx context.Context, limit, offset int32) ([]domain.VirtualGameProvider, int64, error) + // SetProviderEnabled(ctx context.Context, providerID string, enabled bool) (*domain.VirtualGameProvider, error) GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) HandleCallback(ctx context.Context, callback *domain.PopOKCallback) error diff --git a/internal/services/virtualGame/veli/client.go b/internal/services/virtualGame/veli/client.go index cf899e0..529dd41 100644 --- a/internal/services/virtualGame/veli/client.go +++ b/internal/services/virtualGame/veli/client.go @@ -88,7 +88,7 @@ func (c *Client) generateSignature(params map[string]any) (string, error) { // POST helper -func (c *Client) post(ctx context.Context, path string, body any, sigParams map[string]any, result any) error { +func (c *Client) Post(ctx context.Context, path string, body any, sigParams map[string]any, result any) error { data, _ := json.Marshal(body) sig, err := c.generateSignature(sigParams) if err != nil { diff --git a/internal/services/virtualGame/veli/game_orchestration.go b/internal/services/virtualGame/veli/game_orchestration.go index 2b340d8..6082148 100644 --- a/internal/services/virtualGame/veli/game_orchestration.go +++ b/internal/services/virtualGame/veli/game_orchestration.go @@ -1,326 +1 @@ package veli - -import ( - "context" - "fmt" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" - "go.uber.org/zap" -) - -func (s *Service) AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) { - - logger := s.mongoLogger.With(zap.String("service", "AddProviders"), zap.Any("ProviderRequest", req)) - - // 0. Remove all existing providers first - if err := s.repo.DeleteAllVirtualGameProviders(ctx); err != nil { - logger.Error("failed to delete all virtual game providers", zap.Error(err)) - return nil, fmt.Errorf("failed to clear existing providers: %w", err) - } - - // 1. Prepare signature parameters - sigParams := map[string]any{ - "brandId": req.BrandID, - } - - // Optional fields - sigParams["extraData"] = fmt.Sprintf("%t", req.ExtraData) // false is still included - if req.Size > 0 { - sigParams["size"] = fmt.Sprintf("%d", req.Size) - } else { - sigParams["size"] = "" - } - - if req.Page > 0 { - sigParams["page"] = fmt.Sprintf("%d", req.Page) - } else { - sigParams["page"] = "" - } - - // 2. Call external API - var res domain.ProviderResponse - if err := s.client.post(ctx, "/game-lists/public/providers", req, sigParams, &res); err != nil { - return nil, fmt.Errorf("failed to fetch providers: %w", err) - } - - // 3. Loop through fetched providers and insert into DB - for _, p := range res.Items { - createParams := dbgen.CreateVirtualGameProviderParams{ - ProviderID: p.ProviderID, - ProviderName: p.ProviderName, - LogoDark: pgtype.Text{String: p.LogoForDark, Valid: p.LogoForDark != ""}, - LogoLight: pgtype.Text{String: p.LogoForLight, Valid: p.LogoForLight != ""}, - Enabled: true, - } - - if _, err := s.repo.CreateVirtualGameProvider(ctx, createParams); err != nil { - logger.Error("failed to add provider", zap.Error(err)) - return nil, fmt.Errorf("failed to add provider %s: %w", p.ProviderID, err) - } - } - - // 4. Always add "popok" provider manually - popokParams := dbgen.CreateVirtualGameProviderParams{ - ProviderID: "popok", - ProviderName: "Popok Gaming", - LogoDark: pgtype.Text{String: fmt.Sprintf("%v/static/logos/popok-dark.png", s.cfg.PopOK.CallbackURL), Valid: true}, // adjust as needed - LogoLight: pgtype.Text{String: fmt.Sprintf("%v/static/logos/popok-light.png", s.cfg.PopOK.CallbackURL), Valid: true}, // adjust as needed - Enabled: true, - } - - atlasParams := dbgen.CreateVirtualGameProviderParams{ - ProviderID: "atlas", - ProviderName: "Atlas Gaming", - LogoDark: pgtype.Text{String: "/static/logos/atlas-dark.png", Valid: true}, // adjust as needed - LogoLight: pgtype.Text{String: "/static/logos/atlas-light.png", Valid: true}, // adjust as needed - Enabled: true, - } - - if _, err := s.repo.CreateVirtualGameProvider(ctx, popokParams); err != nil { - logger.Error("failed to add popok provider", zap.Any("popokParams", popokParams), zap.Error(err)) - return nil, fmt.Errorf("failed to add popok provider: %w", err) - } - - if _, err := s.repo.CreateVirtualGameProvider(ctx, atlasParams); err != nil { - return nil, fmt.Errorf("failed to add atlas provider: %w", err) - } - - // Optionally also append it to the response for consistency - // res.Items = append(res.Items, domain.VirtualGameProvider{ - // ProviderID: uuid.New().String(), - // ProviderName: "Popok Gaming", - // LogoForDark: "/static/logos/popok-dark.png", - // LogoForLight: "/static/logos/popok-light.png", - // }) - - return &res, nil -} - -func (s *Service) GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error) { - // Build params for repo call - logger := s.mongoLogger.With(zap.String("service", "GetAllVirtualGames"), zap.Any("params", params)) - rows, err := s.repo.ListAllVirtualGames(ctx, params) - if err != nil { - logger.Error("[GetAllVirtualGames] Failed to fetch virtual games", zap.Error(err)) - return nil, fmt.Errorf("failed to fetch virtual games: %w", err) - } - - var allGames []domain.UnifiedGame - for _, r := range rows { - // --- Convert nullable Rtp to *float64 --- - var rtpPtr *float64 - if r.Rtp.Valid { - rtpFloat, err := r.Rtp.Float64Value() - if err == nil { - rtpPtr = new(float64) - *rtpPtr = rtpFloat.Float64 - } - } - var betsFloat64 []float64 - for _, bet := range r.Bets { - if bet.Valid { - betFloat, err := bet.Float64Value() - if err == nil { - betsFloat64 = append(betsFloat64, betFloat.Float64) - } - } - } - - allGames = append(allGames, domain.UnifiedGame{ - GameID: r.GameID, - ProviderID: r.ProviderID, - Provider: r.ProviderName, - Name: r.Name, - Category: r.Category.String, - DeviceType: r.DeviceType.String, - Volatility: r.Volatility.String, - RTP: rtpPtr, - HasDemo: r.HasDemo.Bool, - HasFreeBets: r.HasFreeBets.Bool, - Bets: betsFloat64, - Thumbnail: r.Thumbnail.String, - Status: int(r.Status.Int32), // nullable status - }) - } - - return allGames, nil -} - -func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) { - logger := s.mongoLogger.With( - zap.String("service", "FetchAndStoreAllVirtualGames"), - zap.Any("ProviderRequest", req), - ) - - // This is necessary since the provider is a foreign key - _, err := s.AddProviders(ctx, req) - if err != nil { - return nil, fmt.Errorf("failed to add providers to database: %w", err) - } - - var allGames []domain.UnifiedGame - - // --- 1. Existing providers (Veli Games) --- - providersRes, err := s.GetProviders(ctx, req) - if err != nil { - logger.Error("Failed to fetch provider", zap.Error(err)) - return nil, fmt.Errorf("failed to fetch providers: %w", err) - } - - // --- 2. Fetch games for each provider (Veli Games) --- - for _, p := range providersRes.Items { - games, err := s.GetGames(ctx, domain.GameListRequest{ - BrandID: s.cfg.VeliGames.BrandID, - ProviderID: p.ProviderID, - Page: req.Page, - Size: req.Size, - }) - if err != nil { - logger.Error("failed to get veli games", zap.String("ProviderID", p.ProviderID), zap.Error(err)) - continue // skip failing provider but continue others - } - - for _, g := range games { - unified := domain.UnifiedGame{ - GameID: g.GameID, - ProviderID: g.ProviderID, - Provider: p.ProviderName, - Name: g.Name, - Category: g.Category, - DeviceType: g.DeviceType, - HasDemo: g.HasDemoMode, - HasFreeBets: g.HasFreeBets, - } - allGames = append(allGames, unified) - - // Save to DB - _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ - GameID: g.GameID, - ProviderID: g.ProviderID, - Name: g.Name, - Category: pgtype.Text{ - String: g.Category, - Valid: g.Category != "", - }, - DeviceType: pgtype.Text{ - String: g.DeviceType, - Valid: g.DeviceType != "", - }, - HasDemo: pgtype.Bool{ - Bool: g.HasDemoMode, - Valid: true, - }, - HasFreeBets: pgtype.Bool{ - Bool: g.HasFreeBets, - Valid: true, - }, - }) - if err != nil { - logger.Error("failed to create virtual game", zap.Error(err)) - } - } - } - - // --- 3. Fetch Atlas-V games --- - atlasGames, err := s.GetAtlasVGames(ctx) - if err != nil { - logger.Error("failed to fetch Atlas-V games", zap.Error(err)) - } else { - for _, g := range atlasGames { - unified := domain.UnifiedGame{ - GameID: g.GameID, - ProviderID: "atlasv", - Provider: "Atlas-V Gaming", // "Atlas-V" - Name: g.Name, - Category: g.Category, // using Type as Category - Thumbnail: g.Thumbnail, - HasDemo: true, - DemoURL: g.DemoURL, - } - allGames = append(allGames, unified) - - // Save to DB - _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ - GameID: g.GameID, - ProviderID: "atlasv", - Name: g.Name, - Category: pgtype.Text{ - String: g.Category, - Valid: g.Category != "", - }, - Thumbnail: pgtype.Text{ - String: g.Thumbnail, - Valid: g.Thumbnail != "", - }, - HasDemo: pgtype.Bool{ - Bool: g.HasDemoMode, - Valid: true, - }, - }) - if err != nil { - logger.Error("failed to create Atlas-V virtual game", zap.Error(err)) - } - } - } - - // --- 4. Handle PopOK separately --- - popokGames, err := s.virtualGameSvc.ListGames(ctx, currency) - if err != nil { - logger.Error("failed to fetch PopOk games", zap.Error(err)) - return nil, fmt.Errorf("failed to fetch PopOK games: %w", err) - } - - for _, g := range popokGames { - unified := domain.UnifiedGame{ - GameID: fmt.Sprintf("%d", g.ID), - ProviderID: "popok", - Provider: "PopOK", - Name: g.GameName, - Category: "Crash", - Bets: g.Bets, - Thumbnail: g.Thumbnail, - Status: g.Status, - } - allGames = append(allGames, unified) - - // Convert []float64 to []pgtype.Numeric - var betsNumeric []pgtype.Numeric - for _, bet := range g.Bets { - var num pgtype.Numeric - _ = num.Scan(bet) - betsNumeric = append(betsNumeric, num) - } - - // Save to DB - _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ - GameID: fmt.Sprintf("%d", g.ID), - ProviderID: "popok", - Name: g.GameName, - Bets: betsNumeric, - Thumbnail: pgtype.Text{ - String: g.Thumbnail, - Valid: g.Thumbnail != "", - }, - Status: pgtype.Int4{ - Int32: int32(g.Status), - Valid: true, - }, - HasDemo: pgtype.Bool{ - Bool: true, - Valid: true, - }, - Category: pgtype.Text{ - String: "Crash", - Valid: true, - }, - - }) - if err != nil { - logger.Error("failed to create PopOK virtual game", zap.Error(err)) - } - } - - return allGames, nil -} diff --git a/internal/services/virtualGame/veli/service.go b/internal/services/virtualGame/veli/service.go index cea093e..3e2992c 100644 --- a/internal/services/virtualGame/veli/service.go +++ b/internal/services/virtualGame/veli/service.go @@ -111,7 +111,7 @@ func (s *Service) GetProviders(ctx context.Context, req domain.ProviderRequest) } var res domain.ProviderResponse - err := s.client.post(ctx, "/game-lists/public/providers", req, sigParams, &res) + err := s.client.Post(ctx, "/game-lists/public/providers", req, sigParams, &res) return &res, err } @@ -139,7 +139,7 @@ func (s *Service) GetGames(ctx context.Context, req domain.GameListRequest) ([]d var res struct { Items []domain.GameEntity `json:"items"` } - if err := s.client.post(ctx, "/game-lists/public/games", req, sigParams, &res); err != nil { + if err := s.client.Post(ctx, "/game-lists/public/games", req, sigParams, &res); err != nil { return nil, fmt.Errorf("failed to fetch games for provider %s: %w", req.ProviderID, err) } @@ -174,7 +174,7 @@ func (s *Service) StartGame(ctx context.Context, req domain.GameStartRequest) (* // 3. Call external API var res domain.GameStartResponse - if err := s.client.post(ctx, "/unified-api/public/start-game", req, sigParams, &res); err != nil { + if err := s.client.Post(ctx, "/unified-api/public/start-game", req, sigParams, &res); err != nil { return nil, fmt.Errorf("failed to start game with provider %s: %w", req.ProviderID, err) } @@ -205,7 +205,7 @@ func (s *Service) StartDemoGame(ctx context.Context, req domain.DemoGameRequest) // 3. Call external API var res domain.GameStartResponse - if err := s.client.post(ctx, "/unified-api/public/start-demo-game", req, sigParams, &res); err != nil { + if err := s.client.Post(ctx, "/unified-api/public/start-demo-game", req, sigParams, &res); err != nil { return nil, fmt.Errorf("failed to start demo game with provider %s: %w", req.ProviderID, err) } @@ -598,7 +598,7 @@ func (s *Service) GetGamingActivity(ctx context.Context, req domain.GamingActivi // --- Actual API Call --- var res domain.GamingActivityResponse - err := s.client.post(ctx, "/report-api/public/gaming-activity", req, sigParams, &res) + err := s.client.Post(ctx, "/report-api/public/gaming-activity", req, sigParams, &res) if err != nil { return nil, err } @@ -639,7 +639,7 @@ func (s *Service) GetHugeWins(ctx context.Context, req domain.HugeWinsRequest) ( // --- Actual API Call --- var res domain.HugeWinsResponse - err := s.client.post(ctx, "/report-api/public/gaming-activity/huge-wins", req, sigParams, &res) + err := s.client.Post(ctx, "/report-api/public/gaming-activity/huge-wins", req, sigParams, &res) if err != nil { return nil, err } @@ -662,7 +662,7 @@ func (s *Service) GetCreditBalances(ctx context.Context, brandID string) ([]doma Credits []domain.CreditBalance `json:"credits"` } - if err := s.client.post(ctx, "/report-api/public/credit/balances", body, nil, &res); err != nil { + if err := s.client.Post(ctx, "/report-api/public/credit/balances", body, nil, &res); err != nil { return nil, fmt.Errorf("failed to fetch credit balances: %w", err) } diff --git a/internal/web_server/app.go b/internal/web_server/app.go index a836706..cf3d6fe 100644 --- a/internal/web_server/app.go +++ b/internal/web_server/app.go @@ -33,6 +33,7 @@ import ( virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/atlas" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" @@ -48,7 +49,8 @@ import ( type App struct { enetPulseSvc *enetpulse.Service atlasVirtualGameService atlas.AtlasVirtualGameService - veliVirtualGameService veli.VeliVirtualGameService + veliVirtualGameService *veli.Service + orchestrationSvc *orchestration.Service telebirrSvc *telebirr.TelebirrService arifpaySvc *arifpay.ArifpayService santimpaySvc *santimpay.SantimPayService @@ -90,7 +92,8 @@ type App struct { func NewApp( enetPulseSvc *enetpulse.Service, atlasVirtualGameService atlas.AtlasVirtualGameService, - veliVirtualGameService veli.VeliVirtualGameService, + veliVirtualGameService *veli.Service, + orchestrationSvc *orchestration.Service, telebirrSvc *telebirr.TelebirrService, arifpaySvc *arifpay.ArifpayService, santimpaySvc *santimpay.SantimPayService, @@ -146,6 +149,7 @@ func NewApp( enetPulseSvc: enetPulseSvc, atlasVirtualGameService: atlasVirtualGameService, veliVirtualGameService: veliVirtualGameService, + orchestrationSvc: orchestrationSvc, telebirrSvc: telebirrSvc, arifpaySvc: arifpaySvc, santimpaySvc: santimpaySvc, diff --git a/internal/web_server/cron.go b/internal/web_server/cron.go index 99ad807..51f225f 100644 --- a/internal/web_server/cron.go +++ b/internal/web_server/cron.go @@ -16,7 +16,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/services/report" resultsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration" "github.com/robfig/cron/v3" "go.uber.org/zap" ) @@ -160,21 +160,18 @@ func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificati func SetupReportandVirtualGameCronJobs( ctx context.Context, reportService *report.Service, - virtualGameService *veli.Service, // inject your virtual game service + virtualGameOrchestrationService *orchestration.Service, outputDir string, ) { - c := cron.New(cron.WithSeconds()) // use WithSeconds for testing + c := cron.New(cron.WithSeconds()) // WithSeconds for testing, remove in prod schedule := []struct { spec string period string }{ - // { - // spec: "*/60 * * * * *", // Every 1 minute for testing - // period: "test", - // }, + // { spec: "*/60 * * * * *", period: "test" }, // every 60 seconds for testing { - spec: "0 0 0 * * *", // Daily at midnight + spec: "0 0 0 * * *", // daily at midnight period: "daily", }, } @@ -183,7 +180,7 @@ func SetupReportandVirtualGameCronJobs( period := job.period if _, err := c.AddFunc(job.spec, func() { - log.Printf("[%s] Running virtual game fetch & store job...", period) + log.Printf("[%s] Running virtual game & provider report job...", period) brandID := os.Getenv("VELI_BRAND_ID") if brandID == "" { @@ -191,6 +188,7 @@ func SetupReportandVirtualGameCronJobs( return } + // Step 1. Fetch and store all virtual games req := domain.ProviderRequest{ BrandID: brandID, ExtraData: true, @@ -198,7 +196,7 @@ func SetupReportandVirtualGameCronJobs( Page: 1, } - allGames, err := virtualGameService.FetchAndStoreAllVirtualGames(ctx, req, "ETB") + allGames, err := virtualGameOrchestrationService.FetchAndStoreAllVirtualGames(ctx, req, "ETB") if err != nil { log.Printf("[%s] Error fetching/storing virtual games: %v", period, err) return @@ -206,19 +204,42 @@ func SetupReportandVirtualGameCronJobs( log.Printf("[%s] Successfully fetched & stored %d virtual games", period, len(allGames)) - // --- Generate reports only for daily runs --- - if period == "daily" { - now := time.Now() - from := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, now.Location()) - to := time.Date(now.Year(), now.Month(), now.Day()-1, 23, 59, 59, 0, now.Location()) - - log.Printf("Running daily report for period %s -> %s", from.Format(time.RFC3339), to.Format(time.RFC3339)) - if err := reportService.GenerateReport(ctx, from, to); err != nil { - log.Printf("Error generating daily report: %v", err) - } else { - log.Printf("Successfully generated daily report") - } + // Step 2. Fetch all providers + providers, total, err := virtualGameOrchestrationService.ListProviders(ctx, 1000, 0) + if err != nil { + log.Printf("[%s] Failed to list providers: %v", period, err) + return + } else if total == 0 { + log.Printf("[%s] No providers found, skipping report generation", period) + return } + + log.Printf("[%s] Found %d total providers", period, total) + + // Step 3. Create provider-level daily report entries + reportDate := time.Now().UTC().Truncate(24 * time.Hour) + for _, p := range providers { + createReq := domain.CreateVirtualGameProviderReport{ + ProviderID: p.ProviderID, + ReportDate: reportDate, + TotalGamesPlayed: 0, + TotalBets: 0, + TotalPayouts: 0, + TotalPlayers: 0, + ReportType: period, // "daily" + } + + _, err := virtualGameOrchestrationService.CreateVirtualGameProviderReport(ctx, createReq) + if err != nil { + log.Printf("[%s] Failed to create report for provider %s: %v", period, p.ProviderID, err) + continue + } + + log.Printf("[%s] Created daily report row for provider: %s", period, p.ProviderID) + } + + log.Printf("[%s] Daily provider reports created successfully", period) + }); err != nil { log.Fatalf("Failed to schedule %s cron job: %v", period, err) } diff --git a/internal/web_server/handlers/atlas.go b/internal/web_server/handlers/atlas.go index 9690578..1b44b69 100644 --- a/internal/web_server/handlers/atlas.go +++ b/internal/web_server/handlers/atlas.go @@ -7,9 +7,11 @@ import ( "fmt" "log" "strings" + "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/gofiber/fiber/v2" + "go.uber.org/zap" ) // GetAtlasVGames godoc @@ -52,7 +54,7 @@ func (h *Handler) GetAtlasVGames(c *fiber.Ctx) error { // @Failure 502 {object} domain.ErrorResponse // @Router /api/v1/atlas/init-game [post] func (h *Handler) InitAtlasGame(c *fiber.Ctx) error { - // Retrieve user ID from context + // 1️⃣ Retrieve user ID from context userId, ok := c.Locals("user_id").(int64) if !ok { return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ @@ -61,6 +63,7 @@ func (h *Handler) InitAtlasGame(c *fiber.Ctx) error { }) } + // 2️⃣ Parse request body var req domain.AtlasGameInitRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ @@ -69,30 +72,53 @@ func (h *Handler) InitAtlasGame(c *fiber.Ctx) error { }) } - // Attach user ID to request + // 3️⃣ Attach user ID to request req.PlayerID = fmt.Sprintf("%d", userId) - // Default language if not provided + // 4️⃣ Set defaults if not provided if req.Language == "" { req.Language = "en" } - - // Default currency if not provided if req.Currency == "" { req.Currency = "USD" } - // Call the service + // 5️⃣ Call the Atlas service res, err := h.atlasVirtualGameSvc.InitGame(context.Background(), req) if err != nil { log.Println("InitAtlasGame error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ Message: "Failed to initialize Atlas game", Error: err.Error(), }) } + // 6️⃣ Update provider report: increment total_games_played + go func() { + ctx := context.Background() + reportDate := time.Now().Truncate(24 * time.Hour) + reportType := "daily" + providerID := "atlas" // all Atlas games belong to this provider + + err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate( + ctx, + providerID, + reportDate, + reportType, + 1, // increment total_games_played by 1 + 0, // total_bets (no change) + 0, // total_payouts (no change) + 1, // total_players (no change) + ) + if err != nil { + h.InternalServerErrorLogger().Error("Failed to update total_games_played for Atlas game", + zap.String("provider_id", providerID), + zap.Error(err), + ) + } + }() + + // 7️⃣ Return response to user return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Game initialized successfully", Data: res, diff --git a/internal/web_server/handlers/handlers.go b/internal/web_server/handlers/handlers.go index 48f60f7..70ecc44 100644 --- a/internal/web_server/handlers/handlers.go +++ b/internal/web_server/handlers/handlers.go @@ -33,6 +33,7 @@ import ( virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/atlas" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" @@ -41,6 +42,7 @@ import ( ) type Handler struct { + orchestrationSvc *orchestration.Service enetPulseSvc *enetpulse.Service telebirrSvc *telebirr.TelebirrService arifpaySvc *arifpay.ArifpayService @@ -53,7 +55,7 @@ type Handler struct { notificationSvc *notificationservice.Service userSvc *user.Service referralSvc *referralservice.Service - raffleSvc raffle.RaffleStore + raffleSvc raffle.RaffleStore bonusSvc *bonus.Service reportSvc report.ReportStore chapaSvc *chapa.Service @@ -68,7 +70,7 @@ type Handler struct { leagueSvc league.Service virtualGameSvc virtualgameservice.VirtualGameService aleaVirtualGameSvc alea.AleaVirtualGameService - veliVirtualGameSvc veli.VeliVirtualGameService + veliVirtualGameSvc *veli.Service atlasVirtualGameSvc atlas.AtlasVirtualGameService recommendationSvc recommendation.RecommendationService authSvc *authentication.Service @@ -80,6 +82,7 @@ type Handler struct { } func New( + orchestrationSvc *orchestration.Service, enetPulseSvc *enetpulse.Service, telebirrSvc *telebirr.TelebirrService, arifpaySvc *arifpay.ArifpayService, @@ -99,7 +102,7 @@ func New( bonusSvc *bonus.Service, virtualGameSvc virtualgameservice.VirtualGameService, aleaVirtualGameSvc alea.AleaVirtualGameService, - veliVirtualGameSvc veli.VeliVirtualGameService, + veliVirtualGameSvc *veli.Service, atlasVirtualGameSvc atlas.AtlasVirtualGameService, recommendationSvc recommendation.RecommendationService, userSvc *user.Service, @@ -118,6 +121,7 @@ func New( mongoLoggerSvc *zap.Logger, ) *Handler { return &Handler{ + orchestrationSvc: orchestrationSvc, enetPulseSvc: enetPulseSvc, telebirrSvc: telebirrSvc, arifpaySvc: arifpaySvc, @@ -132,7 +136,7 @@ func New( chapaSvc: chapaSvc, walletSvc: walletSvc, referralSvc: referralSvc, - raffleSvc: raffleSvc, + raffleSvc: raffleSvc, bonusSvc: bonusSvc, validator: validator, userSvc: userSvc, diff --git a/internal/web_server/handlers/veli_games.go b/internal/web_server/handlers/veli_games.go index 8232ee6..5bf3c1b 100644 --- a/internal/web_server/handlers/veli_games.go +++ b/internal/web_server/handlers/veli_games.go @@ -3,6 +3,8 @@ package handlers import ( "context" "errors" + "time" + // "fmt" "strings" @@ -120,15 +122,6 @@ func (h *Handler) GetGamesByProvider(c *fiber.Ctx) error { // @Failure 502 {object} domain.ErrorResponse // @Router /api/v1/veli/start-game [post] func (h *Handler) StartGame(c *fiber.Ctx) error { - // userId, ok := c.Locals("user_id").(int64) - // fmt.Printf("\n\nVeli Start Game User ID is %v\n\n", userId) - // if !ok { - // return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ - // Error: "missing user id", - // Message: "Unauthorized", - // }) - // } - var req domain.GameStartRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ @@ -137,11 +130,6 @@ func (h *Handler) StartGame(c *fiber.Ctx) error { }) } - // There needs to be a way to generate a sessionID - - // Attach user ID to request - // req.PlayerID = fmt.Sprintf("%d", userId) - // Default brand if not provided if req.BrandID == "" { req.BrandID = h.Cfg.VeliGames.BrandID @@ -149,13 +137,14 @@ func (h *Handler) StartGame(c *fiber.Ctx) error { req.IP = c.IP() + // 1️⃣ Call StartGame service res, err := h.veliVirtualGameSvc.StartGame(context.Background(), req) if err != nil { h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]StartGame", zap.Any("request", req), zap.Error(err), ) - // Handle provider disabled case specifically + if strings.Contains(err.Error(), "is disabled") { return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{ Message: "Provider is disabled", @@ -163,13 +152,39 @@ func (h *Handler) StartGame(c *fiber.Ctx) error { }) } - // Fallback for other errors return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ Message: "Failed to start game", Error: err.Error(), }) } + // 2️⃣ Game started successfully → Update total_games_played + go func() { + ctx := context.Background() + reportDate := time.Now().Truncate(24 * time.Hour) + reportType := "daily" + + // Increment total_games_played by 1 + err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate( + ctx, + req.ProviderID, + reportDate, + reportType, + 1, // increment total_games_played by 1 + 0, + 0, + 1, + ) + + if err != nil { + h.InternalServerErrorLogger().Error("Failed to update total_games_played", + zap.String("provider_id", req.ProviderID), + zap.Error(err), + ) + } + }() + + // 3️⃣ Return response to user return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Game started successfully", Data: res, @@ -261,15 +276,13 @@ func (h *Handler) GetBalance(c *fiber.Ctx) error { func (h *Handler) PlaceBet(c *fiber.Ctx) error { var req domain.BetRequest if err := c.BodyParser(&req); err != nil { - // return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid request body", Error: err.Error(), }) } - // Signature check optional here - + // 1️⃣ Process the bet with the external provider res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), req) if err != nil { if errors.Is(err, veli.ErrDuplicateTransaction) { @@ -279,10 +292,42 @@ func (h *Handler) PlaceBet(c *fiber.Ctx) error { Message: "Failed to process bet", Error: err.Error(), }) - // return fiber.NewError(fiber.StatusBadRequest, err.Error()) } - return c.JSON(res) + // 2️⃣ If bet successful → update total_bets in the report + go func() { + ctx := context.Background() + reportDate := time.Now().Truncate(24 * time.Hour) + reportType := "daily" + + // Increment total_bets by the bet amount + err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate( + ctx, + req.ProviderID, + reportDate, + reportType, + 0, // total_games_played (no change) + req.Amount.Amount, // add this bet to total_bets + 0, // total_payouts (no change) + 0, // total_players (no change) + ) + + if err != nil { + h.InternalServerErrorLogger().Error("Failed to update total_bets after bet", + zap.String("provider_id", req.ProviderID), + zap.Float64("bet_amount", req.Amount.Amount), + zap.Error(err), + ) + } + }() + + // 3️⃣ Return success response + return c.Status(fiber.StatusOK).JSON(domain.Response{ + Message: "Bet processed successfully", + Data: res, + StatusCode: fiber.StatusOK, + Success: true, + }) } func (h *Handler) RegisterWin(c *fiber.Ctx) error { diff --git a/internal/web_server/handlers/virtual_games_hadlers.go b/internal/web_server/handlers/virtual_games_hadlers.go index 8c1fdd3..c03c1a2 100644 --- a/internal/web_server/handlers/virtual_games_hadlers.go +++ b/internal/web_server/handlers/virtual_games_hadlers.go @@ -25,6 +25,54 @@ type launchVirtualGameRes struct { LaunchURL string `json:"launch_url"` } +// ListVirtualGameProviderReportsAscHandler +// @Summary List virtual game provider reports (ascending) +// @Description Retrieves all virtual game provider reports sorted by total_games_played in ascending order +// @Tags VirtualGames - Orchestration +// @Success 200 {array} domain.VirtualGameProviderReport +// @Failure 500 {object} domain.ErrorResponse +// @Router /api/v1/orchestrator/virtual-game/provider-reports/asc [get] +func (h *Handler) ListVirtualGameProviderReportsAscHandler(c *fiber.Ctx) error { + reports, err := h.orchestrationSvc.ListVirtualGameProviderReportsByGamesPlayedAsc(c.Context()) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ + Message: "Failed to fetch virtual game provider reports ascending", + Error: err.Error(), + }) + } + + return c.Status(fiber.StatusOK).JSON(domain.Response{ + Message: "Virtual game provider reports retrieved successfully", + Data: reports, + StatusCode: fiber.StatusOK, + Success: true, + }) +} + +// ListVirtualGameProviderReportsDescHandler +// @Summary List virtual game provider reports (descending) +// @Description Retrieves all virtual game provider reports sorted by total_games_played in descending order +// @Tags VirtualGames - Orchestration +// @Success 200 {array} domain.VirtualGameProviderReport +// @Failure 500 {object} domain.ErrorResponse +// @Router /api/v1/orchestrator/virtual-game/provider-reports/desc [get] +func (h *Handler) ListVirtualGameProviderReportsDescHandler(c *fiber.Ctx) error { + reports, err := h.orchestrationSvc.ListVirtualGameProviderReportsByGamesPlayedDesc(c.Context()) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ + Message: "Failed to fetch virtual game provider reports descending", + Error: err.Error(), + }) + } + + return c.Status(fiber.StatusOK).JSON(domain.Response{ + Message: "Virtual game provider reports retrieved successfully", + Data: reports, + StatusCode: fiber.StatusOK, + Success: true, + }) +} + // ListVirtualGames godoc // @Summary List all virtual games // @Description Returns all virtual games with optional filters (category, search, pagination) @@ -77,7 +125,7 @@ func (h *Handler) ListVirtualGames(c *fiber.Ctx) error { } // --- Call service method --- - games, err := h.veliVirtualGameSvc.GetAllVirtualGames(c.Context(), params) + games, err := h.orchestrationSvc.GetAllVirtualGames(c.Context(), params) if err != nil { log.Println("ListVirtualGames error:", err) return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ @@ -105,7 +153,7 @@ func (h *Handler) ListVirtualGames(c *fiber.Ctx) error { // @Router /api/v1/virtual-game/providers/{provider_id} [delete] func (h *Handler) RemoveProvider(c *fiber.Ctx) error { providerID := c.Params("providerID") - if err := h.virtualGameSvc.RemoveProvider(c.Context(), providerID); err != nil { + if err := h.orchestrationSvc.RemoveProvider(c.Context(), providerID); err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Could not remove provider") } return c.SendStatus(fiber.StatusOK) @@ -122,7 +170,7 @@ func (h *Handler) RemoveProvider(c *fiber.Ctx) error { // @Router /api/v1/virtual-game/providers/{provider_id} [get] func (h *Handler) GetProviderByID(c *fiber.Ctx) error { providerID := c.Params("providerID") - provider, err := h.virtualGameSvc.GetProviderByID(c.Context(), providerID) + provider, err := h.orchestrationSvc.GetProviderByID(c.Context(), providerID) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Could not fetch provider") } @@ -143,7 +191,7 @@ func (h *Handler) ListProviders(c *fiber.Ctx) error { limit, _ := strconv.Atoi(c.Query("limit", "20")) offset, _ := strconv.Atoi(c.Query("offset", "0")) - providers, total, err := h.virtualGameSvc.ListProviders(c.Context(), int32(limit), int32(offset)) + providers, total, err := h.orchestrationSvc.ListProviders(c.Context(), int32(limit), int32(offset)) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Could not list providers") } @@ -168,7 +216,7 @@ func (h *Handler) SetProviderEnabled(c *fiber.Ctx) error { providerID := c.Params("providerID") enabled, _ := strconv.ParseBool(c.Query("enabled", "true")) - provider, err := h.virtualGameSvc.SetProviderEnabled(c.Context(), providerID, enabled) + provider, err := h.orchestrationSvc.SetProviderEnabled(c.Context(), providerID, enabled) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Could not update provider status") } diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index c1e8146..27f8099 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -20,6 +20,7 @@ import ( func (a *App) initAppRoutes() { h := handlers.New( + a.orchestrationSvc, a.enetPulseSvc, a.telebirrSvc, a.arifpaySvc, @@ -467,6 +468,8 @@ func (a *App) initAppRoutes() { groupV1.Delete("/virtual-game/favorites/:gameID", a.authMiddleware, h.RemoveFavorite) groupV1.Get("/virtual-game/favorites", a.authMiddleware, h.ListFavorites) + groupV1.Get("/orchestrator/virtual-game/provider-reports/asc", a.OnlyAdminAndAbove, h.ListVirtualGameProviderReportsAscHandler) + groupV1.Get("/orchestrator/virtual-game/provider-reports/desc", a.OnlyAdminAndAbove, h.ListVirtualGameProviderReportsDescHandler) groupV1.Delete("/virtual-game/orchestrator/providers/:provideID", a.authMiddleware, h.RemoveProvider) groupV1.Get("/virtual-game/orchestrator/providers/:provideID", a.authMiddleware, h.GetProviderByID) groupV1.Get("/virtual-game/orchestrator/games", h.ListVirtualGames)