Yimaru-BackEnd/internal/repository/virtual_report.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
}