527 lines
16 KiB
Go
527 lines
16 KiB
Go
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
|
|
}
|