Merge branch 'main' into ticket-bet
This commit is contained in:
commit
8ca4758917
|
|
@ -153,7 +153,7 @@ func main() {
|
||||||
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
|
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
|
||||||
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
||||||
veliCLient := veli.NewClient(cfg, walletSvc)
|
veliCLient := veli.NewClient(cfg, walletSvc)
|
||||||
veliVirtualGameService := veli.New(veliCLient, walletSvc, cfg)
|
veliVirtualGameService := veli.New(veliCLient, walletSvc, wallet.TransferStore(store), cfg)
|
||||||
recommendationSvc := recommendation.NewService(recommendationRepo)
|
recommendationSvc := recommendation.NewService(recommendationRepo)
|
||||||
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
|
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
|
||||||
|
|
||||||
|
|
@ -187,7 +187,7 @@ func main() {
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
|
|
||||||
go httpserver.SetupReportCronJobs(context.Background(), reportSvc)
|
go httpserver.SetupReportCronJobs(context.Background(), reportSvc, "C:/Users/User/Desktop")
|
||||||
go httpserver.ProcessBetCashback(context.TODO(), betSvc)
|
go httpserver.ProcessBetCashback(context.TODO(), betSvc)
|
||||||
|
|
||||||
bankRepository := repository.NewBankRepository(store)
|
bankRepository := repository.NewBankRepository(store)
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,57 @@
|
||||||
|
|
||||||
-- name: GetCompanyWiseReport :many
|
-- name: GetCompanyWiseReport :many
|
||||||
SELECT b.company_id,
|
SELECT
|
||||||
c.name AS company_name,
|
b.company_id,
|
||||||
COUNT(*) AS total_bets,
|
c.name AS company_name,
|
||||||
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
COUNT(*) AS total_bets,
|
||||||
COALESCE(
|
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||||
SUM(
|
COALESCE(
|
||||||
CASE
|
SUM(
|
||||||
WHEN b.cashed_out THEN b.amount
|
CASE
|
||||||
ELSE 0
|
WHEN sb.cashed_out THEN b.amount -- use actual cashed_out flag from shop_bets
|
||||||
END
|
ELSE 0
|
||||||
),
|
END
|
||||||
0
|
), 0
|
||||||
) AS total_cash_out,
|
) AS total_cash_out,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
SUM(
|
SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN b.status = 5 THEN b.amount
|
WHEN b.status = 5 THEN b.amount
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END
|
END
|
||||||
),
|
), 0
|
||||||
0
|
) AS total_cash_backs
|
||||||
) AS total_cash_backs
|
|
||||||
FROM shop_bet_detail b
|
FROM shop_bet_detail b
|
||||||
JOIN companies c ON b.company_id = c.id
|
JOIN companies c ON b.company_id = c.id
|
||||||
|
JOIN shop_bets sb ON sb.id = b.id -- join to get cashed_out
|
||||||
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
||||||
GROUP BY b.company_id,
|
GROUP BY b.company_id, c.name;
|
||||||
c.name;
|
|
||||||
-- name: GetBranchWiseReport :many
|
-- name: GetBranchWiseReport :many
|
||||||
SELECT b.branch_id,
|
SELECT
|
||||||
br.name AS branch_name,
|
b.branch_id,
|
||||||
br.company_id,
|
br.name AS branch_name,
|
||||||
COUNT(*) AS total_bets,
|
br.company_id,
|
||||||
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
COUNT(*) AS total_bets,
|
||||||
COALESCE(
|
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||||
SUM(
|
COALESCE(
|
||||||
CASE
|
SUM(
|
||||||
WHEN b.cashed_out THEN b.amount
|
CASE
|
||||||
ELSE 0
|
WHEN sb.cashed_out THEN b.amount -- use cashed_out from shop_bets
|
||||||
END
|
ELSE 0
|
||||||
),
|
END
|
||||||
0
|
), 0
|
||||||
) AS total_cash_out,
|
) AS total_cash_out,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
SUM(
|
SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN b.status = 5 THEN b.amount
|
WHEN b.status = 5 THEN b.amount
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END
|
END
|
||||||
),
|
), 0
|
||||||
0
|
) AS total_cash_backs
|
||||||
) AS total_cash_backs
|
|
||||||
FROM shop_bet_detail b
|
FROM shop_bet_detail b
|
||||||
JOIN branches br ON b.branch_id = br.id
|
JOIN branches br ON b.branch_id = br.id
|
||||||
|
JOIN shop_bets sb ON sb.id = b.id -- join to get cashed_out
|
||||||
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
||||||
GROUP BY b.branch_id,
|
GROUP BY b.branch_id, br.name, br.company_id;
|
||||||
br.name,
|
|
||||||
br.company_id;
|
|
||||||
|
|
|
||||||
|
|
@ -12,35 +12,33 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const GetBranchWiseReport = `-- name: GetBranchWiseReport :many
|
const GetBranchWiseReport = `-- name: GetBranchWiseReport :many
|
||||||
SELECT b.branch_id,
|
SELECT
|
||||||
br.name AS branch_name,
|
b.branch_id,
|
||||||
br.company_id,
|
br.name AS branch_name,
|
||||||
COUNT(*) AS total_bets,
|
br.company_id,
|
||||||
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
COUNT(*) AS total_bets,
|
||||||
COALESCE(
|
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||||
SUM(
|
COALESCE(
|
||||||
CASE
|
SUM(
|
||||||
WHEN b.cashed_out THEN b.amount
|
CASE
|
||||||
ELSE 0
|
WHEN sb.cashed_out THEN b.amount -- use cashed_out from shop_bets
|
||||||
END
|
ELSE 0
|
||||||
),
|
END
|
||||||
0
|
), 0
|
||||||
) AS total_cash_out,
|
) AS total_cash_out,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
SUM(
|
SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN b.status = 5 THEN b.amount
|
WHEN b.status = 5 THEN b.amount
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END
|
END
|
||||||
),
|
), 0
|
||||||
0
|
) AS total_cash_backs
|
||||||
) AS total_cash_backs
|
|
||||||
FROM shop_bet_detail b
|
FROM shop_bet_detail b
|
||||||
JOIN branches br ON b.branch_id = br.id
|
JOIN branches br ON b.branch_id = br.id
|
||||||
|
JOIN shop_bets sb ON sb.id = b.id -- join to get cashed_out
|
||||||
WHERE b.created_at BETWEEN $1 AND $2
|
WHERE b.created_at BETWEEN $1 AND $2
|
||||||
GROUP BY b.branch_id,
|
GROUP BY b.branch_id, br.name, br.company_id
|
||||||
br.name,
|
|
||||||
br.company_id
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetBranchWiseReportParams struct {
|
type GetBranchWiseReportParams struct {
|
||||||
|
|
@ -87,33 +85,32 @@ func (q *Queries) GetBranchWiseReport(ctx context.Context, arg GetBranchWiseRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetCompanyWiseReport = `-- name: GetCompanyWiseReport :many
|
const GetCompanyWiseReport = `-- name: GetCompanyWiseReport :many
|
||||||
SELECT b.company_id,
|
SELECT
|
||||||
c.name AS company_name,
|
b.company_id,
|
||||||
COUNT(*) AS total_bets,
|
c.name AS company_name,
|
||||||
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
COUNT(*) AS total_bets,
|
||||||
COALESCE(
|
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
||||||
SUM(
|
COALESCE(
|
||||||
CASE
|
SUM(
|
||||||
WHEN b.cashed_out THEN b.amount
|
CASE
|
||||||
ELSE 0
|
WHEN sb.cashed_out THEN b.amount -- use actual cashed_out flag from shop_bets
|
||||||
END
|
ELSE 0
|
||||||
),
|
END
|
||||||
0
|
), 0
|
||||||
) AS total_cash_out,
|
) AS total_cash_out,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
SUM(
|
SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN b.status = 5 THEN b.amount
|
WHEN b.status = 5 THEN b.amount
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END
|
END
|
||||||
),
|
), 0
|
||||||
0
|
) AS total_cash_backs
|
||||||
) AS total_cash_backs
|
|
||||||
FROM shop_bet_detail b
|
FROM shop_bet_detail b
|
||||||
JOIN companies c ON b.company_id = c.id
|
JOIN companies c ON b.company_id = c.id
|
||||||
|
JOIN shop_bets sb ON sb.id = b.id -- join to get cashed_out
|
||||||
WHERE b.created_at BETWEEN $1 AND $2
|
WHERE b.created_at BETWEEN $1 AND $2
|
||||||
GROUP BY b.company_id,
|
GROUP BY b.company_id, c.name
|
||||||
c.name
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetCompanyWiseReportParams struct {
|
type GetCompanyWiseReportParams struct {
|
||||||
|
|
|
||||||
|
|
@ -488,6 +488,7 @@ type LiveWalletMetrics struct {
|
||||||
BranchBalances []BranchWalletBalance `json:"branch_balances"`
|
BranchBalances []BranchWalletBalance `json:"branch_balances"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Company-level aggregated report
|
||||||
type CompanyReport struct {
|
type CompanyReport struct {
|
||||||
CompanyID int64
|
CompanyID int64
|
||||||
CompanyName string
|
CompanyName string
|
||||||
|
|
@ -497,6 +498,7 @@ type CompanyReport struct {
|
||||||
TotalCashBacks float64
|
TotalCashBacks float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Branch-level aggregated report
|
||||||
type BranchReport struct {
|
type BranchReport struct {
|
||||||
BranchID int64
|
BranchID int64
|
||||||
BranchName string
|
BranchName string
|
||||||
|
|
@ -506,3 +508,14 @@ type BranchReport struct {
|
||||||
TotalCashOut float64
|
TotalCashOut float64
|
||||||
TotalCashBacks float64
|
TotalCashBacks float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// type CompanyReport struct {
|
||||||
|
// CompanyID int64
|
||||||
|
// Branches []BranchReport
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type BranchReport struct {
|
||||||
|
// BranchID int64
|
||||||
|
// Rows []interface{} // replace with the actual row type, e.g., dbgen.GetWalletTransactionsInRangeRow
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ type CancelRequest struct {
|
||||||
CorrelationID string `json:"correlationId,omitempty"`
|
CorrelationID string `json:"correlationId,omitempty"`
|
||||||
ProviderID string `json:"providerId"`
|
ProviderID string `json:"providerId"`
|
||||||
BrandID string `json:"brandId"`
|
BrandID string `json:"brandId"`
|
||||||
|
IsAdjustment bool `json:"isAdjustment,omitempty"`
|
||||||
AdjustmentRefund *struct {
|
AdjustmentRefund *struct {
|
||||||
Amount float64 `json:"amount"`
|
Amount float64 `json:"amount"`
|
||||||
Currency string `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
|
|
|
||||||
|
|
@ -459,87 +459,76 @@ func (s *Service) GetSportPerformance(ctx context.Context, filter domain.ReportF
|
||||||
return performances, nil
|
return performances, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GenerateReport(ctx context.Context, period string) error {
|
func (s *Service) GenerateReport(ctx context.Context, from, to time.Time) error {
|
||||||
data, err := s.fetchReportData(ctx, period)
|
// Hardcoded output directory
|
||||||
|
outputDir := "C:/Users/User/Desktop/reports"
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
if err := os.MkdirAll(outputDir, os.ModePerm); err != nil {
|
||||||
|
return fmt.Errorf("failed to create report directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
companies, branchMap, err := s.fetchReportData(ctx, from, to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("fetch data: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the reports directory exists
|
// per-company reports
|
||||||
if err := os.MkdirAll("reports", os.ModePerm); err != nil {
|
for _, company := range companies {
|
||||||
return fmt.Errorf("creating reports directory: %w", err)
|
branches := branchMap[company.CompanyID]
|
||||||
|
if err := writeCompanyCSV(company, branches, from, to, outputDir); err != nil {
|
||||||
|
return fmt.Errorf("company %d CSV: %w", company.CompanyID, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath := fmt.Sprintf("reports/report_%s_%s.csv", period, time.Now().Format("2006-01-02_15-04"))
|
// summary report
|
||||||
|
if err := writeSummaryCSV(companies, from, to, outputDir); err != nil {
|
||||||
|
return fmt.Errorf("summary CSV: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeCompanyCSV writes the company report to CSV in the hardcoded folder
|
||||||
|
func writeCompanyCSV(company domain.CompanyReport, branches []domain.BranchReport, from, to time.Time, outputDir string) error {
|
||||||
|
period := fmt.Sprintf("%s to %s", from.Format("2006-01-02"), to.Format("2006-01-02"))
|
||||||
|
|
||||||
|
filePath := fmt.Sprintf("%s/company_%d_%s_%s_%s.csv",
|
||||||
|
outputDir,
|
||||||
|
company.CompanyID,
|
||||||
|
from.Format("2006-01-02"),
|
||||||
|
to.Format("2006-01-02"),
|
||||||
|
time.Now().Format("2006-01-02_15-04"),
|
||||||
|
)
|
||||||
|
|
||||||
file, err := os.Create(filePath)
|
file, err := os.Create(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create file: %w", err)
|
return fmt.Errorf("create company csv: %w", err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
writer := csv.NewWriter(file)
|
writer := csv.NewWriter(file)
|
||||||
defer writer.Flush()
|
defer writer.Flush()
|
||||||
|
|
||||||
// Summary section
|
// Company summary section
|
||||||
if err := writer.Write([]string{"Sports Betting Reports (Periodic)"}); err != nil {
|
writer.Write([]string{"Company Betting Report"})
|
||||||
return fmt.Errorf("write header: %w", err)
|
writer.Write([]string{"Period", "Company ID", "Company Name", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
||||||
}
|
writer.Write([]string{
|
||||||
if err := writer.Write([]string{"Period", "Total Bets", "Total Cash Made", "Total Cash Out", "Total Cash Backs", "Total Deposits", "Total Withdrawals", "Total Tickets"}); err != nil {
|
|
||||||
return fmt.Errorf("write header row: %w", err)
|
|
||||||
}
|
|
||||||
if err := writer.Write([]string{
|
|
||||||
period,
|
period,
|
||||||
fmt.Sprintf("%d", data.TotalBets),
|
fmt.Sprintf("%d", company.CompanyID),
|
||||||
fmt.Sprintf("%.2f", data.TotalCashIn),
|
company.CompanyName,
|
||||||
fmt.Sprintf("%.2f", data.TotalCashOut),
|
fmt.Sprintf("%d", company.TotalBets),
|
||||||
fmt.Sprintf("%.2f", data.CashBacks),
|
fmt.Sprintf("%.2f", company.TotalCashIn),
|
||||||
fmt.Sprintf("%.2f", data.Deposits),
|
fmt.Sprintf("%.2f", company.TotalCashOut),
|
||||||
fmt.Sprintf("%.2f", data.Withdrawals),
|
fmt.Sprintf("%.2f", company.TotalCashBacks),
|
||||||
fmt.Sprintf("%d", data.TotalTickets),
|
})
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("write summary row: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write([]string{}) // Empty line
|
writer.Write([]string{}) // Empty line
|
||||||
|
|
||||||
// Virtual Game Summary section
|
// Branch reports
|
||||||
writer.Write([]string{"Virtual Game Reports (Periodic)"})
|
writer.Write([]string{"Branch Reports"})
|
||||||
writer.Write([]string{"Game Name", "Number of Bets", "Total Transaction Sum"})
|
|
||||||
for _, row := range data.VirtualGameStats {
|
|
||||||
if err := writer.Write([]string{
|
|
||||||
row.GameName,
|
|
||||||
fmt.Sprintf("%d", row.NumBets),
|
|
||||||
fmt.Sprintf("%.2f", row.TotalTransaction),
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("write virtual game row: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write([]string{}) // Empty line
|
|
||||||
|
|
||||||
// Company Reports
|
|
||||||
writer.Write([]string{"Company Reports (Periodic)"})
|
|
||||||
writer.Write([]string{"Company ID", "Company Name", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
|
||||||
for _, cr := range data.CompanyReports {
|
|
||||||
if err := writer.Write([]string{
|
|
||||||
fmt.Sprintf("%d", cr.CompanyID),
|
|
||||||
cr.CompanyName,
|
|
||||||
fmt.Sprintf("%d", cr.TotalBets),
|
|
||||||
fmt.Sprintf("%.2f", cr.TotalCashIn),
|
|
||||||
fmt.Sprintf("%.2f", cr.TotalCashOut),
|
|
||||||
fmt.Sprintf("%.2f", cr.TotalCashBacks),
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("write company row: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write([]string{}) // Empty line
|
|
||||||
|
|
||||||
// Branch Reports
|
|
||||||
writer.Write([]string{"Branch Reports (Periodic)"})
|
|
||||||
writer.Write([]string{"Branch ID", "Branch Name", "Company ID", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
writer.Write([]string{"Branch ID", "Branch Name", "Company ID", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
||||||
for _, br := range data.BranchReports {
|
for _, br := range branches {
|
||||||
if err := writer.Write([]string{
|
writer.Write([]string{
|
||||||
fmt.Sprintf("%d", br.BranchID),
|
fmt.Sprintf("%d", br.BranchID),
|
||||||
br.BranchName,
|
br.BranchName,
|
||||||
fmt.Sprintf("%d", br.CompanyID),
|
fmt.Sprintf("%d", br.CompanyID),
|
||||||
|
|
@ -547,211 +536,142 @@ func (s *Service) GenerateReport(ctx context.Context, period string) error {
|
||||||
fmt.Sprintf("%.2f", br.TotalCashIn),
|
fmt.Sprintf("%.2f", br.TotalCashIn),
|
||||||
fmt.Sprintf("%.2f", br.TotalCashOut),
|
fmt.Sprintf("%.2f", br.TotalCashOut),
|
||||||
fmt.Sprintf("%.2f", br.TotalCashBacks),
|
fmt.Sprintf("%.2f", br.TotalCashBacks),
|
||||||
}); err != nil {
|
})
|
||||||
return fmt.Errorf("write branch row: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Total Summary
|
if err := writer.Error(); err != nil {
|
||||||
|
return fmt.Errorf("flush error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeSummaryCSV writes the summary report to CSV in the hardcoded folder
|
||||||
|
func writeSummaryCSV(companies []domain.CompanyReport, from, to time.Time, outputDir string) error {
|
||||||
|
period := fmt.Sprintf("%s to %s", from.Format("2006-01-02"), to.Format("2006-01-02"))
|
||||||
|
|
||||||
|
filePath := fmt.Sprintf("%s/summary_%s_%s_%s.csv",
|
||||||
|
outputDir,
|
||||||
|
from.Format("2006-01-02"),
|
||||||
|
to.Format("2006-01-02"),
|
||||||
|
time.Now().Format("2006-01-02_15-04"),
|
||||||
|
)
|
||||||
|
|
||||||
|
file, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create summary csv: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
writer := csv.NewWriter(file)
|
||||||
|
defer writer.Flush()
|
||||||
|
|
||||||
|
// Global summary
|
||||||
|
writer.Write([]string{"Global Betting Summary"})
|
||||||
|
writer.Write([]string{"Period", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
||||||
|
|
||||||
var totalBets int64
|
var totalBets int64
|
||||||
var totalCashIn, totalCashOut, totalCashBacks float64
|
var totalIn, totalOut, totalBack float64
|
||||||
for _, cr := range data.CompanyReports {
|
for _, c := range companies {
|
||||||
totalBets += cr.TotalBets
|
totalBets += c.TotalBets
|
||||||
totalCashIn += cr.TotalCashIn
|
totalIn += c.TotalCashIn
|
||||||
totalCashOut += cr.TotalCashOut
|
totalOut += c.TotalCashOut
|
||||||
totalCashBacks += cr.TotalCashBacks
|
totalBack += c.TotalCashBacks
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write([]string{}) // Empty line
|
writer.Write([]string{
|
||||||
writer.Write([]string{"Total Summary"})
|
period,
|
||||||
writer.Write([]string{"Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
|
||||||
if err := writer.Write([]string{
|
|
||||||
fmt.Sprintf("%d", totalBets),
|
fmt.Sprintf("%d", totalBets),
|
||||||
fmt.Sprintf("%.2f", totalCashIn),
|
fmt.Sprintf("%.2f", totalIn),
|
||||||
fmt.Sprintf("%.2f", totalCashOut),
|
fmt.Sprintf("%.2f", totalOut),
|
||||||
fmt.Sprintf("%.2f", totalCashBacks),
|
fmt.Sprintf("%.2f", totalBack),
|
||||||
}); err != nil {
|
})
|
||||||
return fmt.Errorf("write total summary row: %w", err)
|
writer.Write([]string{}) // Empty line
|
||||||
|
|
||||||
|
// Company breakdown
|
||||||
|
writer.Write([]string{"Company Reports"})
|
||||||
|
writer.Write([]string{"Company ID", "Company Name", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"})
|
||||||
|
for _, cr := range companies {
|
||||||
|
writer.Write([]string{
|
||||||
|
fmt.Sprintf("%d", cr.CompanyID),
|
||||||
|
cr.CompanyName,
|
||||||
|
fmt.Sprintf("%d", cr.TotalBets),
|
||||||
|
fmt.Sprintf("%.2f", cr.TotalCashIn),
|
||||||
|
fmt.Sprintf("%.2f", cr.TotalCashOut),
|
||||||
|
fmt.Sprintf("%.2f", cr.TotalCashBacks),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Error(); err != nil {
|
||||||
|
return fmt.Errorf("flush error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (s *Service) fetchReportData(ctx context.Context, period string) (domain.ReportData, error) {
|
func (s *Service) fetchReportData(ctx context.Context, from, to time.Time) (
|
||||||
from, to := getTimeRange(period)
|
[]domain.CompanyReport, map[int64][]domain.BranchReport, error,
|
||||||
// companyID := int64(0)
|
) {
|
||||||
|
// --- company level ---
|
||||||
// Basic metrics
|
companyRows, err := s.repo.GetCompanyWiseReport(ctx, from, to)
|
||||||
totalBets, _ := s.repo.GetTotalBetsMadeInRange(ctx, from, to)
|
if err != nil {
|
||||||
cashIn, _ := s.repo.GetTotalCashMadeInRange(ctx, from, to)
|
return nil, nil, fmt.Errorf("company-wise report: %w", err)
|
||||||
cashOut, _ := s.repo.GetTotalCashOutInRange(ctx, from, to)
|
|
||||||
cashBacks, _ := s.repo.GetTotalCashBacksInRange(ctx, from, to)
|
|
||||||
|
|
||||||
// Wallet Transactions
|
|
||||||
transactions, _ := s.repo.GetWalletTransactionsInRange(ctx, from, to)
|
|
||||||
var totalDeposits, totalWithdrawals float64
|
|
||||||
for _, tx := range transactions {
|
|
||||||
switch strings.ToLower(tx.Type.String) {
|
|
||||||
case "deposit":
|
|
||||||
totalDeposits += float64(tx.TotalAmount)
|
|
||||||
case "withdraw":
|
|
||||||
totalWithdrawals += float64(tx.TotalAmount)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ticket Count
|
companies := make([]domain.CompanyReport, 0, len(companyRows))
|
||||||
totalTickets, _ := s.repo.GetAllTicketsInRange(ctx, from, to)
|
|
||||||
|
|
||||||
// Virtual Game Summary
|
|
||||||
virtualGameStats, _ := s.repo.GetVirtualGameSummaryInRange(ctx, from, to)
|
|
||||||
|
|
||||||
// Convert []dbgen.GetVirtualGameSummaryInRangeRow to []domain.VirtualGameStat
|
|
||||||
var virtualGameStatsDomain []domain.VirtualGameStat
|
|
||||||
for _, row := range virtualGameStats {
|
|
||||||
var totalTransaction float64
|
|
||||||
switch v := row.TotalTransactionSum.(type) {
|
|
||||||
case string:
|
|
||||||
val, err := strconv.ParseFloat(v, 64)
|
|
||||||
if err == nil {
|
|
||||||
totalTransaction = val
|
|
||||||
}
|
|
||||||
case float64:
|
|
||||||
totalTransaction = v
|
|
||||||
case int:
|
|
||||||
totalTransaction = float64(v)
|
|
||||||
default:
|
|
||||||
totalTransaction = 0
|
|
||||||
}
|
|
||||||
virtualGameStatsDomain = append(virtualGameStatsDomain, domain.VirtualGameStat{
|
|
||||||
GameName: row.GameName,
|
|
||||||
NumBets: row.NumberOfBets,
|
|
||||||
TotalTransaction: totalTransaction,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
companyRows, _ := s.repo.GetCompanyWiseReport(ctx, from, to)
|
|
||||||
var companyReports []domain.CompanyReport
|
|
||||||
for _, row := range companyRows {
|
for _, row := range companyRows {
|
||||||
var totalCashIn, totalCashOut, totalCashBacks float64
|
companies = append(companies, domain.CompanyReport{
|
||||||
switch v := row.TotalCashMade.(type) {
|
|
||||||
case string:
|
|
||||||
val, err := strconv.ParseFloat(v, 64)
|
|
||||||
if err == nil {
|
|
||||||
totalCashIn = val
|
|
||||||
}
|
|
||||||
case float64:
|
|
||||||
totalCashIn = v
|
|
||||||
case int:
|
|
||||||
totalCashIn = float64(v)
|
|
||||||
default:
|
|
||||||
totalCashIn = 0
|
|
||||||
}
|
|
||||||
switch v := row.TotalCashOut.(type) {
|
|
||||||
case string:
|
|
||||||
val, err := strconv.ParseFloat(v, 64)
|
|
||||||
if err == nil {
|
|
||||||
totalCashOut = val
|
|
||||||
}
|
|
||||||
case float64:
|
|
||||||
totalCashOut = v
|
|
||||||
case int:
|
|
||||||
totalCashOut = float64(v)
|
|
||||||
default:
|
|
||||||
totalCashOut = 0
|
|
||||||
}
|
|
||||||
switch v := row.TotalCashBacks.(type) {
|
|
||||||
case string:
|
|
||||||
val, err := strconv.ParseFloat(v, 64)
|
|
||||||
if err == nil {
|
|
||||||
totalCashBacks = val
|
|
||||||
}
|
|
||||||
case float64:
|
|
||||||
totalCashBacks = v
|
|
||||||
case int:
|
|
||||||
totalCashBacks = float64(v)
|
|
||||||
default:
|
|
||||||
totalCashBacks = 0
|
|
||||||
}
|
|
||||||
companyReports = append(companyReports, domain.CompanyReport{
|
|
||||||
CompanyID: row.CompanyID,
|
CompanyID: row.CompanyID,
|
||||||
CompanyName: row.CompanyName,
|
CompanyName: row.CompanyName,
|
||||||
TotalBets: row.TotalBets,
|
TotalBets: row.TotalBets,
|
||||||
TotalCashIn: totalCashIn,
|
TotalCashIn: toFloat(row.TotalCashMade),
|
||||||
TotalCashOut: totalCashOut,
|
TotalCashOut: toFloat(row.TotalCashOut),
|
||||||
TotalCashBacks: totalCashBacks,
|
TotalCashBacks: toFloat(row.TotalCashBacks),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
branchRows, _ := s.repo.GetBranchWiseReport(ctx, from, to)
|
// --- branch level ---
|
||||||
var branchReports []domain.BranchReport
|
branchRows, err := s.repo.GetBranchWiseReport(ctx, from, to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("branch-wise report: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
branchMap := make(map[int64][]domain.BranchReport)
|
||||||
for _, row := range branchRows {
|
for _, row := range branchRows {
|
||||||
var totalCashIn, totalCashOut, totalCashBacks float64
|
branch := domain.BranchReport{
|
||||||
switch v := row.TotalCashMade.(type) {
|
|
||||||
case string:
|
|
||||||
val, err := strconv.ParseFloat(v, 64)
|
|
||||||
if err == nil {
|
|
||||||
totalCashIn = val
|
|
||||||
}
|
|
||||||
case float64:
|
|
||||||
totalCashIn = v
|
|
||||||
case int:
|
|
||||||
totalCashIn = float64(v)
|
|
||||||
default:
|
|
||||||
totalCashIn = 0
|
|
||||||
}
|
|
||||||
switch v := row.TotalCashOut.(type) {
|
|
||||||
case string:
|
|
||||||
val, err := strconv.ParseFloat(v, 64)
|
|
||||||
if err == nil {
|
|
||||||
totalCashOut = val
|
|
||||||
}
|
|
||||||
case float64:
|
|
||||||
totalCashOut = v
|
|
||||||
case int:
|
|
||||||
totalCashOut = float64(v)
|
|
||||||
default:
|
|
||||||
totalCashOut = 0
|
|
||||||
}
|
|
||||||
switch v := row.TotalCashBacks.(type) {
|
|
||||||
case string:
|
|
||||||
val, err := strconv.ParseFloat(v, 64)
|
|
||||||
if err == nil {
|
|
||||||
totalCashBacks = val
|
|
||||||
}
|
|
||||||
case float64:
|
|
||||||
totalCashBacks = v
|
|
||||||
case int:
|
|
||||||
totalCashBacks = float64(v)
|
|
||||||
default:
|
|
||||||
totalCashBacks = 0
|
|
||||||
}
|
|
||||||
branchReports = append(branchReports, domain.BranchReport{
|
|
||||||
BranchID: row.BranchID,
|
BranchID: row.BranchID,
|
||||||
BranchName: row.BranchName,
|
BranchName: row.BranchName,
|
||||||
CompanyID: row.CompanyID,
|
CompanyID: row.CompanyID,
|
||||||
TotalBets: row.TotalBets,
|
TotalBets: row.TotalBets,
|
||||||
TotalCashIn: totalCashIn,
|
TotalCashIn: toFloat(row.TotalCashMade),
|
||||||
TotalCashOut: totalCashOut,
|
TotalCashOut: toFloat(row.TotalCashOut),
|
||||||
TotalCashBacks: totalCashBacks,
|
TotalCashBacks: toFloat(row.TotalCashBacks),
|
||||||
})
|
}
|
||||||
|
branchMap[row.CompanyID] = append(branchMap[row.CompanyID], branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return domain.ReportData{
|
return companies, branchMap, nil
|
||||||
TotalBets: totalBets,
|
|
||||||
TotalCashIn: cashIn,
|
|
||||||
TotalCashOut: cashOut,
|
|
||||||
CashBacks: cashBacks,
|
|
||||||
Deposits: totalDeposits,
|
|
||||||
Withdrawals: totalWithdrawals,
|
|
||||||
TotalTickets: totalTickets.TotalTickets,
|
|
||||||
VirtualGameStats: virtualGameStatsDomain,
|
|
||||||
CompanyReports: companyReports,
|
|
||||||
BranchReports: branchReports,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTimeRange(period string) (time.Time, time.Time) {
|
// helper to unify float conversions
|
||||||
|
func toFloat(val interface{}) float64 {
|
||||||
|
switch v := val.(type) {
|
||||||
|
case string:
|
||||||
|
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
return v
|
||||||
|
case int:
|
||||||
|
return float64(v)
|
||||||
|
case int64:
|
||||||
|
return float64(v)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTimeRange(period string) (time.Time, time.Time) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
switch strings.ToLower(period) {
|
switch strings.ToLower(period) {
|
||||||
case "daily":
|
case "daily":
|
||||||
|
|
@ -809,3 +729,31 @@ func calculatePerformanceScore(perf domain.BranchPerformance) float64 {
|
||||||
|
|
||||||
return profitScore + customerScore + betScore + winRateScore
|
return profitScore + customerScore + betScore + winRateScore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toCompanyReport converts grouped data into []CompanyReport
|
||||||
|
// func toCompanyReport(grouped map[int64]map[int64][]interface{}) []domain.CompanyReport {
|
||||||
|
// companyReports := []domain.CompanyReport{}
|
||||||
|
// for companyID, branches := range grouped {
|
||||||
|
// companyReport := domain.CompanyReport{
|
||||||
|
// CompanyID: companyID,
|
||||||
|
// Branches: []domain.BranchReport{},
|
||||||
|
// }
|
||||||
|
// for branchID, rows := range branches {
|
||||||
|
// branchReport := domain.BranchReport{
|
||||||
|
// BranchID: branchID,
|
||||||
|
// Rows: rows,
|
||||||
|
// }
|
||||||
|
// companyReport.Branches = append(companyReport.Branches, branchReport)
|
||||||
|
// }
|
||||||
|
// companyReports = append(companyReports, companyReport)
|
||||||
|
// }
|
||||||
|
// return companyReports
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // toBranchReport converts []interface{} to []BranchReport
|
||||||
|
// func toBranchReport(rows []interface{}, branchID int64) domain.BranchReport {
|
||||||
|
// return domain.BranchReport{
|
||||||
|
// BranchID: branchID,
|
||||||
|
// Rows: rows,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,18 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
client *Client
|
client *Client
|
||||||
walletSvc *wallet.Service
|
walletSvc *wallet.Service
|
||||||
cfg *config.Config
|
transfetStore wallet.TransferStore
|
||||||
|
cfg *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(client *Client, walletSvc *wallet.Service, cfg *config.Config) VeliVirtualGameService {
|
func New(client *Client, walletSvc *wallet.Service, transferStore wallet.TransferStore, cfg *config.Config) VeliVirtualGameService {
|
||||||
return &service{
|
return &service{
|
||||||
client: client,
|
client: client,
|
||||||
walletSvc: walletSvc,
|
walletSvc: walletSvc,
|
||||||
cfg: cfg,
|
transfetStore: transferStore,
|
||||||
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,7 +340,7 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
// --- 1. Validate PlayerID ---
|
// --- 1. Validate PlayerID ---
|
||||||
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
|
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid PlayerID %s", req.PlayerID)
|
return nil, fmt.Errorf("invalid PlayerID %q", req.PlayerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 2. Get player wallets ---
|
// --- 2. Get player wallets ---
|
||||||
|
|
@ -358,18 +360,23 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
bonusBalance = float64(playerWallets[1].Balance)
|
bonusBalance = float64(playerWallets[1].Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 3. Refund handling ---
|
// --- 3. Determine refund amount based on IsAdjustment ---
|
||||||
var refundAmount float64
|
var refundAmount float64
|
||||||
if req.AdjustmentRefund.Amount > 0 {
|
if req.IsAdjustment {
|
||||||
|
if req.AdjustmentRefund.Amount <= 0 {
|
||||||
|
return nil, fmt.Errorf("missing adjustmentRefund for adjustment cancel")
|
||||||
|
}
|
||||||
refundAmount = req.AdjustmentRefund.Amount
|
refundAmount = req.AdjustmentRefund.Amount
|
||||||
} else {
|
} else {
|
||||||
// If cancelType = CANCEL_BET and no explicit adjustmentRefund,
|
// Regular cancel: fetch original bet amount if needed
|
||||||
// we may need to look up the original bet transaction and refund that.
|
originalTransfer, err := s.transfetStore.GetTransferByReference(ctx, req.RefTransactionID)
|
||||||
// TODO: implement transaction lookup if required by your domain.
|
if err != nil {
|
||||||
return nil, fmt.Errorf("missing adjustmentRefund for CANCEL_BET")
|
return nil, fmt.Errorf("failed to get original bet for cancellation: %w", err)
|
||||||
|
}
|
||||||
|
refundAmount = float64(originalTransfer.Amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, we assume refund goes back fully to real wallet
|
// --- 4. Refund to wallet ---
|
||||||
usedReal := refundAmount
|
usedReal := refundAmount
|
||||||
usedBonus := 0.0
|
usedBonus := 0.0
|
||||||
|
|
||||||
|
|
@ -392,7 +399,7 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
return nil, fmt.Errorf("failed to refund wallet: %w", err)
|
return nil, fmt.Errorf("failed to refund wallet: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 4. Reload balances after refund ---
|
// --- 5. Reload balances after refund ---
|
||||||
updatedWallets, err := s.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
|
updatedWallets, err := s.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to reload balances: %w", err)
|
return nil, fmt.Errorf("failed to reload balances: %w", err)
|
||||||
|
|
@ -405,11 +412,11 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
bonusBalance = float64(updatedWallets[1].Balance)
|
bonusBalance = float64(updatedWallets[1].Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 5. Build response ---
|
// --- 6. Build response ---
|
||||||
res := &domain.CancelResponse{
|
res := &domain.CancelResponse{
|
||||||
WalletTransactionID: req.TransactionID,
|
WalletTransactionID: req.TransactionID,
|
||||||
Real: domain.BalanceDetail{
|
Real: domain.BalanceDetail{
|
||||||
Currency: req.AdjustmentRefund.Currency,
|
Currency: "ETB",
|
||||||
Amount: realBalance,
|
Amount: realBalance,
|
||||||
},
|
},
|
||||||
UsedRealAmount: usedReal,
|
UsedRealAmount: usedReal,
|
||||||
|
|
@ -418,7 +425,7 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
|
|
||||||
if bonusBalance > 0 {
|
if bonusBalance > 0 {
|
||||||
res.Bonus = &domain.BalanceDetail{
|
res.Bonus = &domain.BalanceDetail{
|
||||||
Currency: req.AdjustmentRefund.Currency,
|
Currency: "ETB",
|
||||||
Amount: bonusBalance,
|
Amount: bonusBalance,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -426,6 +433,12 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Example helper to fetch original bet
|
||||||
|
// func (s *service) getOriginalBet(ctx context.Context, transactionID string) (*domain.BetRecord, error) {
|
||||||
|
// // TODO: implement actual lookup
|
||||||
|
// return &domain.BetRecord{Amount: 50}, nil
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *service) GetGamingActivity(ctx context.Context, req domain.GamingActivityRequest) (*domain.GamingActivityResponse, error) {
|
func (s *service) GetGamingActivity(ctx context.Context, req domain.GamingActivityRequest) (*domain.GamingActivityResponse, error) {
|
||||||
// --- Signature Params (flattened strings for signing) ---
|
// --- Signature Params (flattened strings for signing) ---
|
||||||
sigParams := map[string]any{
|
sigParams := map[string]any{
|
||||||
|
|
|
||||||
|
|
@ -140,15 +140,16 @@ func StartTicketCrons(ticketService ticket.Service, mongoLogger *zap.Logger) {
|
||||||
mongoLogger.Info("Cron jobs started for ticket service")
|
mongoLogger.Info("Cron jobs started for ticket service")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupReportCronJobs(ctx context.Context, reportService *report.Service) {
|
// SetupReportCronJobs schedules periodic report generation
|
||||||
c := cron.New(cron.WithSeconds()) // use WithSeconds for tighter intervals during testing
|
func SetupReportCronJobs(ctx context.Context, reportService *report.Service, outputDir string) {
|
||||||
|
c := cron.New(cron.WithSeconds()) // use WithSeconds for testing
|
||||||
|
|
||||||
schedule := []struct {
|
schedule := []struct {
|
||||||
spec string
|
spec string
|
||||||
period string
|
period string
|
||||||
}{
|
}{
|
||||||
// {
|
// {
|
||||||
// spec: "*/300 * * * * *", // Every 5 minutes (300 seconds)
|
// spec: "*/60 * * * * *", // Every 5 minutes
|
||||||
// period: "5min",
|
// period: "5min",
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
|
|
@ -167,9 +168,36 @@ func SetupReportCronJobs(ctx context.Context, reportService *report.Service) {
|
||||||
|
|
||||||
for _, job := range schedule {
|
for _, job := range schedule {
|
||||||
period := job.period
|
period := job.period
|
||||||
|
|
||||||
if _, err := c.AddFunc(job.spec, func() {
|
if _, err := c.AddFunc(job.spec, func() {
|
||||||
log.Printf("Running %s report at %s", period, time.Now().Format(time.RFC3339))
|
now := time.Now()
|
||||||
if err := reportService.GenerateReport(ctx, period); err != nil {
|
var from, to time.Time
|
||||||
|
|
||||||
|
switch period {
|
||||||
|
// case "5min":
|
||||||
|
// from = now.Add(-5 * time.Minute)
|
||||||
|
// to = now
|
||||||
|
case "daily":
|
||||||
|
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())
|
||||||
|
case "weekly":
|
||||||
|
// last Sunday -> Saturday
|
||||||
|
weekday := int(now.Weekday())
|
||||||
|
daysSinceSunday := (weekday + 7) % 7
|
||||||
|
from = time.Date(now.Year(), now.Month(), now.Day()-daysSinceSunday-7, 0, 0, 0, 0, now.Location())
|
||||||
|
to = from.AddDate(0, 0, 6).Add(time.Hour*23 + time.Minute*59 + time.Second*59)
|
||||||
|
case "monthly":
|
||||||
|
firstOfMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||||
|
from = firstOfMonth.AddDate(0, -1, 0)
|
||||||
|
to = firstOfMonth.Add(-time.Second)
|
||||||
|
default:
|
||||||
|
log.Printf("Unknown period: %s", period)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Running %s report for period %s -> %s", period, from.Format(time.RFC3339), to.Format(time.RFC3339))
|
||||||
|
|
||||||
|
if err := reportService.GenerateReport(ctx, from, to); err != nil {
|
||||||
log.Printf("Error generating %s report: %v", period, err)
|
log.Printf("Error generating %s report: %v", period, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Successfully generated %s report", period)
|
log.Printf("Successfully generated %s report", period)
|
||||||
|
|
@ -180,7 +208,7 @@ func SetupReportCronJobs(ctx context.Context, reportService *report.Service) {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Start()
|
c.Start()
|
||||||
log.Println("Cron jobs started for report generation service")
|
log.Printf("Cron jobs started. Reports will be saved to: %s", outputDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessBetCashback(ctx context.Context, betService *betSvc.Service) {
|
func ProcessBetCashback(ctx context.Context, betService *betSvc.Service) {
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ func (h *Handler) DownloadReportFile(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
reportDir := "reports"
|
reportDir := "C:/Users/User/Desktop/reports"
|
||||||
|
|
||||||
// Ensure reports directory exists
|
// Ensure reports directory exists
|
||||||
if _, err := os.Stat(reportDir); os.IsNotExist(err) {
|
if _, err := os.Stat(reportDir); os.IsNotExist(err) {
|
||||||
|
|
@ -207,7 +207,7 @@ func (h *Handler) DownloadReportFile(c *fiber.Ctx) error {
|
||||||
// @Failure 500 {object} domain.ErrorResponse "Failed to read report directory"
|
// @Failure 500 {object} domain.ErrorResponse "Failed to read report directory"
|
||||||
// @Router /api/v1/report-files/list [get]
|
// @Router /api/v1/report-files/list [get]
|
||||||
func (h *Handler) ListReportFiles(c *fiber.Ctx) error {
|
func (h *Handler) ListReportFiles(c *fiber.Ctx) error {
|
||||||
reportDir := "reports"
|
reportDir := "C:/Users/User/Desktop/reports"
|
||||||
searchTerm := c.Query("search")
|
searchTerm := c.Query("search")
|
||||||
page := c.QueryInt("page", 1)
|
page := c.QueryInt("page", 1)
|
||||||
limit := c.QueryInt("limit", 20)
|
limit := c.QueryInt("limit", 20)
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ func (h *Handler) HandlePlayerInfo(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
// Read the raw body to avoid parsing issues
|
// Read the raw body
|
||||||
body := c.Body()
|
body := c.Body()
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
|
@ -119,26 +119,11 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to identify the provider based on the request structure
|
// Try parsing as Veli bet request
|
||||||
provider, err := identifyBetProvider(body)
|
var veliReq domain.BetRequest
|
||||||
if err != nil {
|
if err := json.Unmarshal(body, &veliReq); err == nil && veliReq.SessionID != "" && veliReq.BrandID != "" {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
// Process as Veli
|
||||||
Message: "Unrecognized request format",
|
res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), veliReq)
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
switch provider {
|
|
||||||
case "veli":
|
|
||||||
var req domain.BetRequest
|
|
||||||
if err := json.Unmarshal(body, &req); err != nil {
|
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
||||||
Message: "Invalid Veli bet request",
|
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, veli.ErrDuplicateTransaction) {
|
if errors.Is(err, veli.ErrDuplicateTransaction) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
||||||
|
|
@ -152,17 +137,13 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return c.JSON(res)
|
return c.JSON(res)
|
||||||
|
}
|
||||||
|
|
||||||
case "popok":
|
// Try parsing as PopOK bet request
|
||||||
var req domain.PopOKBetRequest
|
var popokReq domain.PopOKBetRequest
|
||||||
if err := json.Unmarshal(body, &req); err != nil {
|
if err := json.Unmarshal(body, &popokReq); err == nil && popokReq.ExternalToken != "" {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
// Process as PopOK
|
||||||
Message: "Invalid PopOK bet request",
|
resp, err := h.virtualGameSvc.ProcessBet(c.Context(), &popokReq)
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := h.virtualGameSvc.ProcessBet(c.Context(), &req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
code := fiber.StatusInternalServerError
|
code := fiber.StatusInternalServerError
|
||||||
switch err.Error() {
|
switch err.Error() {
|
||||||
|
|
@ -177,13 +158,13 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return c.JSON(resp)
|
return c.JSON(resp)
|
||||||
|
|
||||||
default:
|
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
||||||
Message: "Unsupported provider",
|
|
||||||
Error: "Request format doesn't match any supported provider",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If neither works
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Unsupported provider",
|
||||||
|
Error: "Request format doesn't match any supported provider",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// identifyProvider examines the request body to determine the provider
|
// identifyProvider examines the request body to determine the provider
|
||||||
|
|
@ -513,26 +494,25 @@ func (h *Handler) ListFavorites(c *fiber.Ctx) error {
|
||||||
return c.Status(fiber.StatusOK).JSON(games)
|
return c.Status(fiber.StatusOK).JSON(games)
|
||||||
}
|
}
|
||||||
|
|
||||||
func identifyBetProvider(body []byte) (string, error) {
|
func IdentifyBetProvider(body []byte) (string, error) {
|
||||||
// Check for Veli signature fields
|
// Check for Veli signature fields
|
||||||
var veliCheck struct {
|
var veliCheck struct {
|
||||||
TransactionID string `json:"transaction_id"`
|
SessionID string `json:"sessionId"`
|
||||||
GameID string `json:"game_id"`
|
BrandID string `json:"brandId"`
|
||||||
}
|
}
|
||||||
if json.Unmarshal(body, &veliCheck) == nil {
|
if json.Unmarshal(body, &veliCheck) == nil {
|
||||||
if veliCheck.TransactionID != "" && veliCheck.GameID != "" {
|
if veliCheck.SessionID != "" && veliCheck.BrandID != "" {
|
||||||
return "veli", nil
|
return "veli", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for PopOK signature fields
|
// Check for PopOK signature fields
|
||||||
var popokCheck struct {
|
var popokCheck struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"externalToken"`
|
||||||
PlayerID string `json:"player_id"`
|
|
||||||
BetAmount float64 `json:"bet_amount"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if json.Unmarshal(body, &popokCheck) == nil {
|
if json.Unmarshal(body, &popokCheck) == nil {
|
||||||
if popokCheck.Token != "" && popokCheck.PlayerID != "" {
|
if popokCheck.Token != "" {
|
||||||
return "popok", nil
|
return "popok", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -544,23 +524,22 @@ func identifyBetProvider(body []byte) (string, error) {
|
||||||
func identifyWinProvider(body []byte) (string, error) {
|
func identifyWinProvider(body []byte) (string, error) {
|
||||||
// Check for Veli signature fields
|
// Check for Veli signature fields
|
||||||
var veliCheck struct {
|
var veliCheck struct {
|
||||||
TransactionID string `json:"transaction_id"`
|
SessionID string `json:"sessionId"`
|
||||||
WinAmount float64 `json:"win_amount"`
|
BrandID string `json:"brandId"`
|
||||||
}
|
}
|
||||||
if json.Unmarshal(body, &veliCheck) == nil {
|
if json.Unmarshal(body, &veliCheck) == nil {
|
||||||
if veliCheck.TransactionID != "" && veliCheck.WinAmount > 0 {
|
if veliCheck.SessionID != "" && veliCheck.BrandID != "" {
|
||||||
return "veli", nil
|
return "veli", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for PopOK signature fields
|
// Check for PopOK signature fields
|
||||||
var popokCheck struct {
|
var popokCheck struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"externalToken"`
|
||||||
PlayerID string `json:"player_id"`
|
|
||||||
WinAmount float64 `json:"win_amount"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if json.Unmarshal(body, &popokCheck) == nil {
|
if json.Unmarshal(body, &popokCheck) == nil {
|
||||||
if popokCheck.Token != "" && popokCheck.PlayerID != "" {
|
if popokCheck.Token != "" {
|
||||||
return "popok", nil
|
return "popok", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -571,24 +550,22 @@ func identifyWinProvider(body []byte) (string, error) {
|
||||||
func identifyCancelProvider(body []byte) (string, error) {
|
func identifyCancelProvider(body []byte) (string, error) {
|
||||||
// Check for Veli cancel signature
|
// Check for Veli cancel signature
|
||||||
var veliCheck struct {
|
var veliCheck struct {
|
||||||
TransactionID string `json:"transaction_id"`
|
SessionID string `json:"sessionId"`
|
||||||
OriginalTxID string `json:"original_transaction_id"`
|
BrandID string `json:"brandId"`
|
||||||
CancelReason string `json:"cancel_reason"`
|
|
||||||
}
|
}
|
||||||
if json.Unmarshal(body, &veliCheck) == nil {
|
if json.Unmarshal(body, &veliCheck) == nil {
|
||||||
if veliCheck.TransactionID != "" && veliCheck.OriginalTxID != "" {
|
if veliCheck.SessionID != "" && veliCheck.BrandID != "" {
|
||||||
return "veli", nil
|
return "veli", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for PopOK cancel signature
|
// Check for PopOK cancel signature
|
||||||
var popokCheck struct {
|
var popokCheck struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"externalToken"`
|
||||||
PlayerID string `json:"player_id"`
|
|
||||||
OriginalTxID string `json:"original_transaction_id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if json.Unmarshal(body, &popokCheck) == nil {
|
if json.Unmarshal(body, &popokCheck) == nil {
|
||||||
if popokCheck.Token != "" && popokCheck.PlayerID != "" && popokCheck.OriginalTxID != "" {
|
if popokCheck.Token != "" {
|
||||||
return "popok", nil
|
return "popok", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,7 @@ func (a *App) initAppRoutes() {
|
||||||
|
|
||||||
//Report Routes
|
//Report Routes
|
||||||
groupV1.Get("/reports/dashboard", a.authMiddleware, a.OnlyAdminAndAbove, h.GetDashboardReport)
|
groupV1.Get("/reports/dashboard", a.authMiddleware, a.OnlyAdminAndAbove, h.GetDashboardReport)
|
||||||
groupV1.Get("/report-files/download/:filename", a.authMiddleware, a.OnlyAdminAndAbove, h.DownloadReportFile)
|
groupV1.Get("/report-files/download/:filename", h.DownloadReportFile)
|
||||||
groupV1.Get("/report-files/list", a.authMiddleware, a.OnlyAdminAndAbove, h.ListReportFiles)
|
groupV1.Get("/report-files/list", a.authMiddleware, a.OnlyAdminAndAbove, h.ListReportFiles)
|
||||||
|
|
||||||
//Alea Play Virtual Game Routes
|
//Alea Play Virtual Game Routes
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user