451 lines
15 KiB
Go
451 lines
15 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
// NewVirtualGameReportStore returns a new VirtualGameReportStore interface
|
|
func NewVirtualGameReportStore(s *Store) ports.VirtualGameReportStore {
|
|
return s
|
|
}
|
|
|
|
// ------------------- Financial Reports -------------------
|
|
func (s *Store) CreateFinancialReport(ctx context.Context, report domain.FinancialReport) (domain.FinancialReport, error) {
|
|
// pgtype.Numeric no longer exposes a Set method in this pgx version;
|
|
// use zero-value pgtype.Numeric here (or replace with a proper conversion helper).
|
|
totalBets := pgtype.Numeric{}
|
|
totalWins := pgtype.Numeric{}
|
|
|
|
// parse report.ReportDate (try RFC3339 then YYYY-MM-DD)
|
|
t, err := time.Parse(time.RFC3339, report.ReportDate)
|
|
if err != nil {
|
|
t, err = time.Parse("2006-01-02", report.ReportDate)
|
|
if err != nil {
|
|
return domain.FinancialReport{}, fmt.Errorf("invalid report date: %w", err)
|
|
}
|
|
}
|
|
|
|
dbReport, err := s.queries.CreateFinancialReport(ctx, dbgen.CreateFinancialReportParams{
|
|
GameID: report.GameID,
|
|
ProviderID: report.ProviderID,
|
|
ReportDate: pgtype.Date{Time: t},
|
|
ReportType: report.ReportType,
|
|
TotalBets: totalBets,
|
|
TotalWins: totalWins,
|
|
})
|
|
if err != nil {
|
|
return domain.FinancialReport{}, fmt.Errorf("failed to create financial report: %w", err)
|
|
}
|
|
return domain.ConvertDBFinancialReport(dbReport), nil
|
|
}
|
|
|
|
func (s *Store) UpsertFinancialReport(ctx context.Context, report domain.FinancialReport) (domain.FinancialReport, error) {
|
|
totalBets := pgtype.Numeric{}
|
|
totalWins := pgtype.Numeric{}
|
|
|
|
// parse report.ReportDate
|
|
t, err := time.Parse(time.RFC3339, report.ReportDate)
|
|
if err != nil {
|
|
t, err = time.Parse("2006-01-02", report.ReportDate)
|
|
if err != nil {
|
|
return domain.FinancialReport{}, fmt.Errorf("invalid report date: %w", err)
|
|
}
|
|
}
|
|
|
|
dbReport, err := s.queries.UpsertFinancialReport(ctx, dbgen.UpsertFinancialReportParams{
|
|
GameID: report.GameID,
|
|
ProviderID: report.ProviderID,
|
|
ReportDate: pgtype.Date{Time: t},
|
|
ReportType: report.ReportType,
|
|
TotalBets: totalBets,
|
|
TotalWins: totalWins,
|
|
})
|
|
if err != nil {
|
|
return domain.FinancialReport{}, fmt.Errorf("failed to upsert financial report: %w", err)
|
|
}
|
|
|
|
return domain.ConvertDBFinancialReport(dbReport), nil
|
|
}
|
|
|
|
// GetFinancialReportByID fetches a single report by its ID
|
|
func (s *Store) GetFinancialReportByID(ctx context.Context, id int64) (domain.FinancialReport, error) {
|
|
dbReport, err := s.queries.GetFinancialReportByID(ctx, id)
|
|
if err != nil {
|
|
return domain.FinancialReport{}, fmt.Errorf("failed to get financial report by ID: %w", err)
|
|
}
|
|
return domain.ConvertDBFinancialReport(dbReport), nil
|
|
}
|
|
|
|
// GetFinancialReportsForGame fetches all reports for a specific game + provider in a date range
|
|
func (s *Store) GetFinancialReportsForGame(ctx context.Context, gameID, providerID string, from, to string) ([]domain.FinancialReport, error) {
|
|
fromDate, err := time.Parse("2006-01-02", from)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid from date: %w", err)
|
|
}
|
|
toDate, err := time.Parse("2006-01-02", to)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid to date: %w", err)
|
|
}
|
|
|
|
dbReports, err := s.queries.GetFinancialReportsForGame(ctx, dbgen.GetFinancialReportsForGameParams{
|
|
GameID: gameID,
|
|
ProviderID: providerID,
|
|
ReportDate: pgtype.Date{Time: fromDate},
|
|
ReportDate_2: pgtype.Date{Time: toDate},
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get financial reports for game: %w", err)
|
|
}
|
|
|
|
var result []domain.FinancialReport
|
|
for _, r := range dbReports {
|
|
result = append(result, domain.ConvertDBFinancialReport(r))
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Store) GetDailyFinancialReports(ctx context.Context, reportDate string) ([]domain.FinancialReport, error) {
|
|
t, err := time.Parse("2006-01-02", reportDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid report date: %w", err)
|
|
}
|
|
|
|
dbReports, err := s.queries.GetDailyFinancialReports(ctx, pgtype.Date{Time: t})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get daily financial reports: %w", err)
|
|
}
|
|
|
|
var result []domain.FinancialReport
|
|
for _, r := range dbReports {
|
|
result = append(result, domain.ConvertDBFinancialReport(r))
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// DeleteFinancialReport deletes a financial report by ID
|
|
func (s *Store) DeleteFinancialReport(ctx context.Context, id int64) error {
|
|
err := s.queries.DeleteFinancialReport(ctx, id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete financial report: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CreateCompanyReport inserts a new company-level financial report
|
|
func (s *Store) CreateCompanyReport(ctx context.Context, report domain.CompanyReport) (domain.CompanyReport, error) {
|
|
// parse report date
|
|
t, err := time.Parse("2006-01-02", report.ReportDate)
|
|
if err != nil {
|
|
return domain.CompanyReport{}, fmt.Errorf("invalid report date: %w", err)
|
|
}
|
|
|
|
dbReport, err := s.queries.CreateCompanyReport(ctx, dbgen.CreateCompanyReportParams{
|
|
CompanyID: report.CompanyID,
|
|
ProviderID: report.ProviderID,
|
|
ReportDate: pgtype.Date{Time: t},
|
|
ReportType: report.ReportType,
|
|
TotalBetAmount: pgtype.Numeric{}, // zero value; set actual value if required
|
|
TotalWinAmount: pgtype.Numeric{},
|
|
})
|
|
if err != nil {
|
|
return domain.CompanyReport{}, fmt.Errorf("failed to create company report: %w", err)
|
|
}
|
|
|
|
return domain.ConvertDBCompanyReport(dbReport), nil
|
|
}
|
|
|
|
func (s *Store) UpsertCompanyReport(ctx context.Context, report domain.CompanyReport) (domain.CompanyReport, error) {
|
|
// Convert report date
|
|
t, err := time.Parse("2006-01-02", report.ReportDate)
|
|
if err != nil {
|
|
t, err = time.Parse(time.RFC3339, report.ReportDate)
|
|
if err != nil {
|
|
return domain.CompanyReport{}, fmt.Errorf("invalid report date: %w", err)
|
|
}
|
|
}
|
|
|
|
dbReport, err := s.queries.UpsertCompanyReport(ctx, dbgen.UpsertCompanyReportParams{
|
|
CompanyID: report.CompanyID,
|
|
ProviderID: report.ProviderID,
|
|
ReportDate: pgtype.Date{Time: t},
|
|
ReportType: report.ReportType,
|
|
TotalBetAmount: func() pgtype.Numeric {
|
|
var n pgtype.Numeric
|
|
_ = n.Scan(report.TotalBetAmount)
|
|
return n
|
|
}(),
|
|
TotalWinAmount: func() pgtype.Numeric {
|
|
var n pgtype.Numeric
|
|
_ = n.Scan(report.TotalWinAmount)
|
|
return n
|
|
}(),
|
|
})
|
|
if err != nil {
|
|
return domain.CompanyReport{}, fmt.Errorf("failed to upsert company report: %w", err)
|
|
}
|
|
|
|
return domain.ConvertDBCompanyReport(dbReport), nil
|
|
}
|
|
|
|
func (s *Store) GetCompanyReportByID(ctx context.Context, id int64) (domain.CompanyReport, error) {
|
|
dbReport, err := s.queries.GetCompanyReportByID(ctx, id)
|
|
if err != nil {
|
|
return domain.CompanyReport{}, fmt.Errorf("failed to get company report by ID: %w", err)
|
|
}
|
|
|
|
return domain.ConvertDBCompanyReport(dbReport), nil
|
|
}
|
|
|
|
func (s *Store) GetCompanyReportsInRange(ctx context.Context, companyID int64, providerID string, startDate, endDate string) ([]domain.CompanyReport, error) {
|
|
start, err := time.Parse("2006-01-02", startDate)
|
|
if err != nil {
|
|
start, err = time.Parse(time.RFC3339, startDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid start date: %w", err)
|
|
}
|
|
}
|
|
|
|
end, err := time.Parse("2006-01-02", endDate)
|
|
if err != nil {
|
|
end, err = time.Parse(time.RFC3339, endDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid end date: %w", err)
|
|
}
|
|
}
|
|
|
|
dbReports, err := s.queries.GetCompanyReportsInRange(ctx, dbgen.GetCompanyReportsInRangeParams{
|
|
CompanyID: companyID,
|
|
ProviderID: providerID,
|
|
ReportDate: pgtype.Date{Time: start},
|
|
ReportDate_2: pgtype.Date{Time: end},
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get company reports in range: %w", err)
|
|
}
|
|
|
|
reports := make([]domain.CompanyReport, 0, len(dbReports))
|
|
for _, r := range dbReports {
|
|
reports = append(reports, domain.ConvertDBCompanyReport(r))
|
|
}
|
|
|
|
return reports, nil
|
|
}
|
|
|
|
func (s *Store) GetCompanyProfitTrend(ctx context.Context, companyID int64, providerID string, startDate, endDate string) ([]domain.CompanyProfitTrend, error) {
|
|
start, err := time.Parse("2006-01-02", startDate)
|
|
if err != nil {
|
|
start, err = time.Parse(time.RFC3339, startDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid start date: %w", err)
|
|
}
|
|
}
|
|
|
|
end, err := time.Parse("2006-01-02", endDate)
|
|
if err != nil {
|
|
end, err = time.Parse(time.RFC3339, endDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid end date: %w", err)
|
|
}
|
|
}
|
|
|
|
rows, err := s.queries.GetCompanyProfitTrend(ctx, dbgen.GetCompanyProfitTrendParams{
|
|
CompanyID: companyID,
|
|
ProviderID: providerID,
|
|
ReportDate: pgtype.Date{Time: start},
|
|
ReportDate_2: pgtype.Date{Time: end},
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get company profit trend: %w", err)
|
|
}
|
|
|
|
trends := make([]domain.CompanyProfitTrend, 0, len(rows))
|
|
for _, r := range rows {
|
|
trends = append(trends, domain.CompanyProfitTrend{
|
|
ReportDate: r.ReportDate.Time.Format("2006-01-02"),
|
|
TotalProfit: float64(r.TotalProfit),
|
|
})
|
|
}
|
|
return trends, nil
|
|
}
|
|
|
|
// CreatePlayerActivityReport inserts a new player activity report
|
|
func (s *Store) CreatePlayerActivityReport(ctx context.Context, report domain.PlayerActivityReport) (domain.PlayerActivityReport, error) {
|
|
t, err := time.Parse("2006-01-02", report.ReportDate)
|
|
if err != nil {
|
|
t, err = time.Parse(time.RFC3339, report.ReportDate)
|
|
if err != nil {
|
|
return domain.PlayerActivityReport{}, fmt.Errorf("invalid report date: %w", err)
|
|
}
|
|
}
|
|
|
|
createParams := dbgen.CreatePlayerActivityReportParams{
|
|
UserID: report.UserID,
|
|
ReportDate: pgtype.Date{Time: t},
|
|
ReportType: report.ReportType,
|
|
TotalDeposits: func() pgtype.Numeric {
|
|
var n pgtype.Numeric
|
|
_ = n.Scan(report.TotalDeposits)
|
|
return n
|
|
}(),
|
|
TotalWithdrawals: func() pgtype.Numeric {
|
|
var n pgtype.Numeric
|
|
_ = n.Scan(report.TotalWithdrawals)
|
|
return n
|
|
}(),
|
|
TotalBetAmount: func() pgtype.Numeric {
|
|
var n pgtype.Numeric
|
|
_ = n.Scan(report.TotalBetAmount)
|
|
return n
|
|
}(),
|
|
TotalWinAmount: func() pgtype.Numeric {
|
|
var n pgtype.Numeric
|
|
_ = n.Scan(report.TotalWinAmount)
|
|
return n
|
|
}(),
|
|
RoundsPlayed: pgtype.Int8{Int64: int64(report.RoundsPlayed)},
|
|
}
|
|
|
|
var dbReport dbgen.VirtualGamePlayerActivityReport
|
|
dbReport, err = s.queries.CreatePlayerActivityReport(ctx, createParams)
|
|
if err != nil {
|
|
// try upsert as a fallback
|
|
upsertParams := dbgen.UpsertPlayerActivityReportParams{
|
|
UserID: report.UserID,
|
|
ReportDate: pgtype.Date{Time: t},
|
|
ReportType: report.ReportType,
|
|
TotalDeposits: pgtype.Numeric{Exp: int32(report.TotalDeposits)},
|
|
TotalWithdrawals: pgtype.Numeric{Exp: int32(report.TotalWithdrawals)},
|
|
TotalBetAmount: pgtype.Numeric{Exp: int32(report.TotalBetAmount)},
|
|
TotalWinAmount: pgtype.Numeric{Exp: int32(report.TotalWinAmount)},
|
|
RoundsPlayed: pgtype.Int8{Int64: int64(report.RoundsPlayed)},
|
|
}
|
|
dbReport, err = s.queries.UpsertPlayerActivityReport(ctx, upsertParams)
|
|
if err != nil {
|
|
return domain.PlayerActivityReport{}, fmt.Errorf("failed to create or upsert player activity report: %w", err)
|
|
}
|
|
}
|
|
|
|
return domain.ConvertDBPlayerActivityReport(dbReport), nil
|
|
}
|
|
|
|
func (s *Store) GetPlayerActivityByID(ctx context.Context, id int64) (domain.PlayerActivityReport, error) {
|
|
dbReport, err := s.queries.GetPlayerActivityByID(ctx, id)
|
|
if err != nil {
|
|
return domain.PlayerActivityReport{}, err
|
|
}
|
|
return domain.ConvertDBPlayerActivityReport(dbReport), nil
|
|
}
|
|
|
|
// GetPlayerActivityByDate fetches a player activity report for a specific user and date
|
|
func (s *Store) GetPlayerActivityByDate(ctx context.Context, userID int64, reportDate, reportType string) (domain.PlayerActivityReport, error) {
|
|
t, err := time.Parse("2006-01-02", reportDate)
|
|
if err != nil {
|
|
t, err = time.Parse(time.RFC3339, reportDate)
|
|
if err != nil {
|
|
return domain.PlayerActivityReport{}, fmt.Errorf("invalid report date: %w", err)
|
|
}
|
|
}
|
|
|
|
dbReport, err := s.queries.GetPlayerActivityByDate(ctx, dbgen.GetPlayerActivityByDateParams{
|
|
UserID: userID,
|
|
ReportDate: pgtype.Date{Time: t},
|
|
ReportType: reportType,
|
|
})
|
|
if err != nil {
|
|
return domain.PlayerActivityReport{}, err
|
|
}
|
|
|
|
return domain.ConvertDBPlayerActivityReport(dbReport), nil
|
|
}
|
|
|
|
// GetPlayerActivityRange fetches all activity reports for a user in a date range
|
|
func (s *Store) GetPlayerActivityRange(ctx context.Context, userID int64, startDate, endDate string) ([]domain.PlayerActivityReport, error) {
|
|
start, err := time.Parse("2006-01-02", startDate)
|
|
if err != nil {
|
|
start, err = time.Parse(time.RFC3339, startDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid start date: %w", err)
|
|
}
|
|
}
|
|
|
|
end, err := time.Parse("2006-01-02", endDate)
|
|
if err != nil {
|
|
end, err = time.Parse(time.RFC3339, endDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid end date: %w", err)
|
|
}
|
|
}
|
|
|
|
dbReports, err := s.queries.GetPlayerActivityRange(ctx, dbgen.GetPlayerActivityRangeParams{
|
|
UserID: userID,
|
|
ReportDate: pgtype.Date{Time: start},
|
|
ReportDate_2: pgtype.Date{Time: end},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
activities := make([]domain.PlayerActivityReport, 0, len(dbReports))
|
|
for _, r := range dbReports {
|
|
activities = append(activities, domain.ConvertDBPlayerActivityReport(r))
|
|
}
|
|
return activities, nil
|
|
}
|
|
|
|
// GetTopPlayersByNetResult returns the top N players by net result in a date range
|
|
func (s *Store) GetTopPlayersByNetResult(ctx context.Context, startDate, endDate string, limit int) ([]domain.TopPlayerNetResult, error) {
|
|
start, err := time.Parse("2006-01-02", startDate)
|
|
if err != nil {
|
|
start, err = time.Parse(time.RFC3339, startDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid start date: %w", err)
|
|
}
|
|
}
|
|
|
|
end, err := time.Parse("2006-01-02", endDate)
|
|
if err != nil {
|
|
end, err = time.Parse(time.RFC3339, endDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid end date: %w", err)
|
|
}
|
|
}
|
|
|
|
rows, err := s.conn.Query(ctx, `SELECT user_id, SUM(net_result) AS total_net
|
|
FROM virtual_game_player_activity_reports
|
|
WHERE report_date BETWEEN $1 AND $2
|
|
GROUP BY user_id
|
|
ORDER BY total_net DESC
|
|
LIMIT $3`, start, end, limit)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch top players: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var topPlayers []domain.TopPlayerNetResult
|
|
for rows.Next() {
|
|
var tp domain.TopPlayerNetResult
|
|
var totalNet pgtype.Numeric
|
|
if err := rows.Scan(&tp.UserID, &totalNet); err != nil {
|
|
return nil, err
|
|
}
|
|
tp.TotalNet = float64(totalNet.Exp)
|
|
topPlayers = append(topPlayers, tp)
|
|
}
|
|
return topPlayers, nil
|
|
}
|
|
|
|
// DeletePlayerActivityReport deletes a player activity report by its ID
|
|
func (s *Store) DeletePlayerActivityReport(ctx context.Context, id int64) error {
|
|
err := s.queries.DeletePlayerActivityReport(ctx, id)
|
|
return err
|
|
}
|