gitignore fix + export report feature
This commit is contained in:
parent
2f2ba65abd
commit
26e85dbfe8
55
.env
Normal file
55
.env
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# REPORT_EXPORT_PATH="C:\\ProgramData\\FortuneBet\\exported_reports" #prod env
|
||||
REPORT_EXPORT_PATH ="./exported_reports" #dev env
|
||||
|
||||
RESEND_SENDER_EMAIL=email
|
||||
RESEND_API_KEY=123
|
||||
|
||||
ENV=development
|
||||
PORT=8080
|
||||
DB_URL=postgresql://root:secret@localhost:5422/gh?sslmode=disable
|
||||
REFRESH_EXPIRY=2592000
|
||||
JWT_KEY=mysecretkey
|
||||
ACCESS_EXPIRY=600
|
||||
LOG_LEVEL=debug
|
||||
AFRO_SMS_API_KEY=1
|
||||
AFRO_SMS_SENDER_NAME=
|
||||
|
||||
AFRO_SMS_RECEIVER_PHONE_NUMBER=
|
||||
BET365_TOKEN=158046-hesJDP2Cay2M5G
|
||||
POPOK_CLIENT_ID=1
|
||||
POPOK_PLATFORM=111
|
||||
POPOK_SECRET_KEY=XwFQ76Y59zBxGryh
|
||||
# POPOK_BASE_URL=https://api.pokgaming.com/game/launch #Production
|
||||
# POPOK_BASE_URL=https://games.pokgaming.com/launch #Production
|
||||
# POPOK_BASE_URL=https://sandbox.pokgaming.com/game/launch #Staging
|
||||
# POPOK_BASE_URL=https://test-api.pokgaming.com/launch #Staging
|
||||
POPOK_BASE_URL=https://st.pokgaming.com/ #Staging
|
||||
|
||||
POPOK_CALLBACK_URL=1
|
||||
|
||||
# Chapa API Configuration
|
||||
CHAPA_BASE_URL="https://api.chapa.co/v1"
|
||||
CHAPA_ENCRYPTION_KEY=zLdYrjnBCknMvFikmP5jBfen
|
||||
CHAPA_PUBLIC_KEY=CHAPUBK_TEST-HJR0qhQRPLTkauNy9Q8UrmskPTOR31aC
|
||||
CHAPA_SECRET_KEY=CHASECK_TEST-q3jypwmFK6XJGYOK3aX4z9Kogd9KaHhF
|
||||
CHAPA_CALLBACK_URL="https://fortunebet.com/api/v1/payments/callback" # Optional
|
||||
CHAPA_RETURN_URL="https://fortunebet.com/api/v1/payment-success" # Optional
|
||||
|
||||
#Alea Play
|
||||
ALEA_ENABLED=true
|
||||
ALEA_BASE_URL=https://api.aleaplay.com
|
||||
ALEA_OPERATOR_ID=operator_id
|
||||
ALEA_SECRET_KEY=hmac_secret
|
||||
ALEA_GAME_LIST_URL=https://api.aleaplay.com/games/list # Optional
|
||||
ALEA_DEFAULT_CURRENCY=USD # Optional (default: USD)
|
||||
ALEA_SESSION_TIMEOUT=24 # Optional (hours, default: 24)
|
||||
ALEA_GAME_ID_AVIATOR=aviator_prod
|
||||
|
||||
|
||||
# Veli Games
|
||||
VELI_ENABLED=true
|
||||
VELI_API_URL=https://api.velitech.games
|
||||
VELI_OPERATOR_KEY=Veli123
|
||||
VELI_SECRET_KEY=hmac_secret
|
||||
VELI_GAME_ID_AVIATOR=veli_aviator_v1
|
||||
VELI_DEFAULT_CURRENCY=USD
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
|
|
@ -1,8 +1,7 @@
|
|||
bin
|
||||
coverage.out
|
||||
coverage
|
||||
.env
|
||||
tmp
|
||||
build
|
||||
*.log
|
||||
db.sql
|
||||
# bin
|
||||
# coverage.out
|
||||
# coverage
|
||||
# .env
|
||||
# tmp
|
||||
# build
|
||||
# *.log
|
||||
117
cmd/main.go
117
cmd/main.go
|
|
@ -17,6 +17,7 @@ import (
|
|||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/infrastructure"
|
||||
customlogger "github.com/SamuelTariku/FortuneBet-Backend/internal/logger"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/logger/mongoLogger"
|
||||
|
||||
|
|
@ -53,6 +54,7 @@ import (
|
|||
httpserver "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server"
|
||||
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
|
||||
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/worker"
|
||||
)
|
||||
|
||||
// @title FortuneBet API
|
||||
|
|
@ -69,7 +71,6 @@ import (
|
|||
// @name Authorization
|
||||
// @BasePath /
|
||||
func main() {
|
||||
|
||||
cfg, err := config.NewConfig()
|
||||
if err != nil {
|
||||
slog.Error("Config error:", "err", err)
|
||||
|
|
@ -92,55 +93,25 @@ func main() {
|
|||
|
||||
zap.ReplaceGlobals(domain.MongoDBLogger)
|
||||
|
||||
// client := mongoLogger.InitDB()
|
||||
// defer func() {
|
||||
// if err := client.Disconnect(context.Background()); err != nil {
|
||||
// slog.Error("Failed to disconnect MongoDB", "error", err)
|
||||
// }
|
||||
// }()
|
||||
|
||||
// // 2. Create MongoDB logger handler
|
||||
// handler, err := mongoLogger.NewMongoHandler("logs", "app_logs", slog.LevelDebug)
|
||||
// if err != nil {
|
||||
// slog.Error("Failed to create MongoDB logger", "error", err)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
||||
// // 3. Set as default logger
|
||||
// tempLogger := slog.New(handler)
|
||||
// slog.SetDefault(tempLogger)
|
||||
|
||||
// // 4. Log examples
|
||||
// tempLogger.Info("Application started", "version", "1.0.0")
|
||||
// slog.Warn("Low disk space", "available_gb", 12.5)
|
||||
// slog.Error("Payment failed", "transaction_id", "tx123", "error", "insufficient funds")
|
||||
|
||||
store := repository.NewStore(db)
|
||||
v := customvalidator.NewCustomValidator(validator.New())
|
||||
|
||||
// Initialize services
|
||||
authSvc := authentication.NewService(store, store, cfg.RefreshExpiry)
|
||||
|
||||
userSvc := user.NewService(store, store, cfg)
|
||||
|
||||
eventSvc := event.New(cfg.Bet365Token, store)
|
||||
oddsSvc := odds.New(store, cfg, logger)
|
||||
ticketSvc := ticket.NewService(store)
|
||||
notificationRepo := repository.NewNotificationRepository(store)
|
||||
virtuaGamesRepo := repository.NewVirtualGameRepository(store)
|
||||
|
||||
notificationSvc := notificationservice.New(notificationRepo, logger, cfg)
|
||||
|
||||
// var betStore bet.BetStore
|
||||
// var walletStore wallet.WalletStore
|
||||
// var transactionStore transaction.TransactionStore
|
||||
// var branchStore branch.BranchStore
|
||||
// var userStore user.UserStore
|
||||
var notificationStore notificationservice.NotificationStore
|
||||
var notificatioStore notificationservice.NotificationStore
|
||||
|
||||
walletSvc := wallet.NewService(
|
||||
wallet.WalletStore(store),
|
||||
wallet.TransferStore(store),
|
||||
notificationStore,
|
||||
notificatioStore,
|
||||
logger,
|
||||
)
|
||||
|
||||
|
|
@ -156,18 +127,8 @@ func main() {
|
|||
|
||||
referalSvc := referralservice.New(referalRepo, *walletSvc, store, cfg, logger)
|
||||
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
|
||||
aleaService := alea.NewAleaPlayService(
|
||||
vitualGameRepo,
|
||||
*walletSvc,
|
||||
cfg,
|
||||
logger,
|
||||
)
|
||||
veliService := veli.NewVeliPlayService(
|
||||
vitualGameRepo,
|
||||
*walletSvc,
|
||||
cfg,
|
||||
logger,
|
||||
)
|
||||
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
||||
veliService := veli.NewVeliPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
||||
recommendationSvc := recommendation.NewService(recommendationRepo)
|
||||
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
|
||||
|
||||
|
|
@ -178,27 +139,35 @@ func main() {
|
|||
chapaClient,
|
||||
)
|
||||
|
||||
// Initialize reporting components
|
||||
reportRepo := repository.NewReportRepo(store)
|
||||
|
||||
reportSvc := report.NewService(
|
||||
bet.BetStore(store), // Must implement BetStore
|
||||
wallet.WalletStore(store), // Must implement WalletStore
|
||||
bet.BetStore(store),
|
||||
wallet.WalletStore(store),
|
||||
transaction.TransactionStore(store),
|
||||
branch.BranchStore(store),
|
||||
user.UserStore(store),
|
||||
reportRepo,
|
||||
company.CompanyStore(store),
|
||||
virtuaGamesRepo,
|
||||
notificationRepo,
|
||||
logger,
|
||||
)
|
||||
|
||||
// reportSvc := report.NewService(
|
||||
// betStore,
|
||||
// walletStore,
|
||||
// transactionStore,
|
||||
// branchStore,
|
||||
// userStore,
|
||||
// logger,
|
||||
// )
|
||||
// Initialize report worker with CSV exporter
|
||||
csvExporter := infrastructure.CSVExporter{
|
||||
ExportPath: cfg.ReportExportPath, // Make sure to add this to your config
|
||||
}
|
||||
|
||||
reportWorker := worker.NewReportWorker(
|
||||
reportSvc,
|
||||
csvExporter,
|
||||
)
|
||||
|
||||
// Start cron jobs for automated reporting
|
||||
|
||||
// Initialize wallet monitoring
|
||||
walletMonitorSvc := monitor.NewService(
|
||||
*walletSvc,
|
||||
*branchSvc,
|
||||
|
|
@ -208,16 +177,44 @@ func main() {
|
|||
)
|
||||
walletMonitorSvc.Start()
|
||||
|
||||
// Start other cron jobs
|
||||
httpserver.StartDataFetchingCrons(eventSvc, oddsSvc, resultSvc)
|
||||
httpserver.StartTicketCrons(*ticketSvc)
|
||||
go httpserver.SetupReportCronJob(reportWorker)
|
||||
|
||||
app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{
|
||||
// Initialize and start HTTP server
|
||||
app := httpserver.NewApp(
|
||||
cfg.Port,
|
||||
v,
|
||||
authSvc,
|
||||
logger,
|
||||
jwtutil.JwtConfig{
|
||||
JwtAccessKey: cfg.JwtKey,
|
||||
JwtAccessExpiry: cfg.AccessExpiry,
|
||||
}, userSvc,
|
||||
ticketSvc, betSvc, reportSvc, chapaSvc, walletSvc, transactionSvc, branchSvc, companySvc, notificationSvc, oddsSvc, eventSvc, leagueSvc, referalSvc, virtualGameSvc, aleaService, veliService, recommendationSvc, resultSvc, cfg)
|
||||
logger.Info("Starting server", "port", cfg.Port)
|
||||
},
|
||||
userSvc,
|
||||
ticketSvc,
|
||||
betSvc,
|
||||
reportSvc, // Make sure httpserver.NewApp accepts this parameter
|
||||
chapaSvc,
|
||||
walletSvc,
|
||||
transactionSvc,
|
||||
branchSvc,
|
||||
companySvc,
|
||||
notificationSvc,
|
||||
oddsSvc,
|
||||
eventSvc,
|
||||
leagueSvc,
|
||||
referalSvc,
|
||||
virtualGameSvc,
|
||||
aleaService,
|
||||
veliService,
|
||||
recommendationSvc,
|
||||
resultSvc,
|
||||
cfg,
|
||||
)
|
||||
|
||||
logger.Info("Starting server", "port", cfg.Port)
|
||||
if err := app.Run(); err != nil {
|
||||
logger.Error("Failed to start server", "error", err)
|
||||
os.Exit(1)
|
||||
|
|
|
|||
3
go.mod
3
go.mod
|
|
@ -72,6 +72,9 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/resend/resend-go/v2 v2.20.0 // direct
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
)
|
||||
|
||||
require go.uber.org/atomic v1.9.0 // indirect
|
||||
|
|
|
|||
13
go.sum
13
go.sum
|
|
@ -25,6 +25,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
|
||||
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
|
|
@ -56,6 +58,7 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
|||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
|
|
@ -81,6 +84,8 @@ github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBF
|
|||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
|
@ -108,6 +113,7 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ
|
|||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/resend/resend-go/v2 v2.20.0 h1:MrIrgV0aHhwRgmcRPw33Nexn6aGJvCvG2XwfFpAMBGM=
|
||||
|
|
@ -117,6 +123,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
|
|
@ -132,6 +140,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/fiber-swagger v1.3.0 h1:RMjIVDleQodNVdKuu7GRs25Eq8RVXK7MwY9f5jbobNg=
|
||||
|
|
@ -165,6 +174,9 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
|||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
|
||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
|
|
@ -237,6 +249,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ type Config struct {
|
|||
JwtKey string
|
||||
LogLevel slog.Level
|
||||
Env string
|
||||
ReportExportPath string `mapstructure:"REPORT_EXPORT_PATH"`
|
||||
AFRO_SMS_API_KEY string
|
||||
AFRO_SMS_SENDER_NAME string
|
||||
AFRO_SMS_RECEIVER_PHONE_NUMBER string
|
||||
|
|
@ -101,6 +102,8 @@ func (c *Config) loadEnv() error {
|
|||
}
|
||||
c.Env = env
|
||||
|
||||
c.ReportExportPath = os.Getenv("REPORT_EXPORT_PATH")
|
||||
|
||||
portStr := os.Getenv("PORT")
|
||||
if portStr == "" {
|
||||
return ErrInvalidPort
|
||||
|
|
|
|||
|
|
@ -2,6 +2,26 @@ package domain
|
|||
|
||||
import "time"
|
||||
|
||||
type TimeFrame string
|
||||
|
||||
const (
|
||||
Daily TimeFrame = "daily"
|
||||
Weekly TimeFrame = "weekly"
|
||||
Monthly TimeFrame = "monthly"
|
||||
)
|
||||
|
||||
type Report struct {
|
||||
ID string
|
||||
TimeFrame TimeFrame
|
||||
PeriodStart time.Time
|
||||
PeriodEnd time.Time
|
||||
TotalBets int
|
||||
TotalCashIn float64
|
||||
TotalCashOut float64
|
||||
TotalCashBack float64
|
||||
GeneratedAt time.Time
|
||||
}
|
||||
|
||||
type DashboardSummary struct {
|
||||
TotalStakes Currency `json:"total_stakes"`
|
||||
TotalBets int64 `json:"total_bets"`
|
||||
|
|
|
|||
48
internal/infrastructure/csv_exporter.go
Normal file
48
internal/infrastructure/csv_exporter.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// infrastructure/csv_exporter.go
|
||||
package infrastructure
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
||||
type CSVExporter struct {
|
||||
ExportPath string
|
||||
}
|
||||
|
||||
func (e *CSVExporter) Export(report *domain.Report) error {
|
||||
filename := e.ExportPath + "/report_" + string(report.TimeFrame) + "_" +
|
||||
time.Now().Format("20060102") + ".csv"
|
||||
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
writer := csv.NewWriter(file)
|
||||
defer writer.Flush()
|
||||
|
||||
// Write header
|
||||
_ = writer.Write([]string{
|
||||
"Time Frame", "Period Start", "Period End",
|
||||
"Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Back",
|
||||
"Generated At",
|
||||
})
|
||||
|
||||
// Write data
|
||||
return writer.Write([]string{
|
||||
string(report.TimeFrame),
|
||||
report.PeriodStart.Format(time.RFC3339),
|
||||
report.PeriodEnd.Format(time.RFC3339),
|
||||
strconv.Itoa(report.TotalBets),
|
||||
strconv.FormatFloat(report.TotalCashIn, 'f', 2, 64),
|
||||
strconv.FormatFloat(report.TotalCashOut, 'f', 2, 64),
|
||||
strconv.FormatFloat(report.TotalCashBack, 'f', 2, 64),
|
||||
report.GeneratedAt.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
107
internal/repository/report.go
Normal file
107
internal/repository/report.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
||||
type ReportRepository interface {
|
||||
GenerateReport(timeFrame domain.TimeFrame, start, end time.Time) (*domain.Report, error)
|
||||
SaveReport(report *domain.Report) error
|
||||
FindReportsByTimeFrame(timeFrame domain.TimeFrame, limit int) ([]*domain.Report, error)
|
||||
}
|
||||
|
||||
type ReportRepo struct {
|
||||
store *Store
|
||||
}
|
||||
|
||||
func NewReportRepo(store *Store) ReportRepository {
|
||||
return &ReportRepo{store: store}
|
||||
}
|
||||
|
||||
func (r *ReportRepo) GenerateReport(timeFrame domain.TimeFrame, start, end time.Time) (*domain.Report, error) {
|
||||
// Implement SQL queries to calculate metrics
|
||||
var report domain.Report
|
||||
|
||||
// Total Bets
|
||||
err := r.store.conn.QueryRow(
|
||||
context.Background(),
|
||||
`SELECT COUNT(*) FROM bets
|
||||
WHERE created_at BETWEEN $1 AND $2`, start, end).Scan(&report.TotalBets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Total Cash In
|
||||
err = r.store.conn.QueryRow(
|
||||
context.Background(),
|
||||
`SELECT COALESCE(SUM(amount), 0) FROM transactions
|
||||
WHERE type = 'stake' AND created_at BETWEEN $1 AND $2`, start, end).Scan(&report.TotalCashIn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Similar queries for Cash Out and Cash Back...
|
||||
|
||||
report.TimeFrame = timeFrame
|
||||
report.PeriodStart = start
|
||||
report.PeriodEnd = end
|
||||
report.GeneratedAt = time.Now()
|
||||
|
||||
return &report, nil
|
||||
}
|
||||
|
||||
func (r *ReportRepo) SaveReport(report *domain.Report) error {
|
||||
_, err := r.store.conn.Exec(
|
||||
context.Background(),
|
||||
`INSERT INTO reports
|
||||
(id, time_frame, period_start, period_end, total_bets, total_cash_in, total_cash_out, total_cash_back, generated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
||||
report.ID, report.TimeFrame, report.PeriodStart, report.PeriodEnd,
|
||||
report.TotalBets, report.TotalCashIn, report.TotalCashOut, report.TotalCashBack, report.GeneratedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *ReportRepo) FindReportsByTimeFrame(timeFrame domain.TimeFrame, limit int) ([]*domain.Report, error) {
|
||||
rows, err := r.store.conn.Query(
|
||||
context.Background(),
|
||||
`SELECT id, time_frame, period_start, period_end, total_bets,
|
||||
total_cash_in, total_cash_out, total_cash_back, generated_at
|
||||
FROM reports
|
||||
WHERE time_frame = $1
|
||||
ORDER BY generated_at DESC
|
||||
LIMIT $2`,
|
||||
timeFrame, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var reports []*domain.Report
|
||||
for rows.Next() {
|
||||
var report domain.Report
|
||||
err := rows.Scan(
|
||||
&report.ID,
|
||||
&report.TimeFrame,
|
||||
&report.PeriodStart,
|
||||
&report.PeriodEnd,
|
||||
&report.TotalBets,
|
||||
&report.TotalCashIn,
|
||||
&report.TotalCashOut,
|
||||
&report.TotalCashBack,
|
||||
&report.GeneratedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reports = append(reports, &report)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reports, nil
|
||||
}
|
||||
|
|
@ -5,15 +5,18 @@ import (
|
|||
"errors"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
|
||||
|
||||
// notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notfication"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
||||
|
||||
// virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
|
||||
)
|
||||
|
|
@ -29,6 +32,7 @@ type Service struct {
|
|||
transactionStore transaction.TransactionStore
|
||||
branchStore branch.BranchStore
|
||||
userStore user.UserStore
|
||||
repo repository.ReportRepository
|
||||
companyStore company.CompanyStore
|
||||
virtulaGamesStore repository.VirtualGameRepository
|
||||
notificationStore repository.NotificationRepository
|
||||
|
|
@ -41,6 +45,7 @@ func NewService(
|
|||
transactionStore transaction.TransactionStore,
|
||||
branchStore branch.BranchStore,
|
||||
userStore user.UserStore,
|
||||
repo repository.ReportRepository,
|
||||
companyStore company.CompanyStore,
|
||||
virtulaGamesStore repository.VirtualGameRepository,
|
||||
notificationStore repository.NotificationRepository,
|
||||
|
|
@ -52,6 +57,7 @@ func NewService(
|
|||
transactionStore: transactionStore,
|
||||
branchStore: branchStore,
|
||||
userStore: userStore,
|
||||
repo: repo,
|
||||
companyStore: companyStore,
|
||||
virtulaGamesStore: virtulaGamesStore,
|
||||
notificationStore: notificationStore,
|
||||
|
|
@ -448,6 +454,34 @@ func (s *Service) GetSportPerformance(ctx context.Context, filter domain.ReportF
|
|||
return performances, nil
|
||||
}
|
||||
|
||||
func (s *Service) GenerateReport(timeFrame domain.TimeFrame) (*domain.Report, error) {
|
||||
now := time.Now()
|
||||
var start, end time.Time
|
||||
|
||||
switch timeFrame {
|
||||
case domain.Daily:
|
||||
start = now.AddDate(0, 0, -1)
|
||||
end = now
|
||||
case domain.Weekly:
|
||||
start = now.AddDate(0, 0, -7)
|
||||
end = now
|
||||
case domain.Monthly:
|
||||
start = now.AddDate(0, -1, 0)
|
||||
end = now
|
||||
}
|
||||
|
||||
report, err := s.repo.GenerateReport(timeFrame, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.repo.SaveReport(report); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return report, nil
|
||||
}
|
||||
|
||||
// func (s *Service) GetCompanyPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CompanyPerformance, error) {
|
||||
// // Get company bet activity
|
||||
// companyBets, err := s.betStore.GetCompanyBetActivity(ctx, filter)
|
||||
|
|
|
|||
|
|
@ -2,18 +2,43 @@ package httpserver
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"log"
|
||||
|
||||
// "time"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||
oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||
resultsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/worker"
|
||||
"github.com/go-co-op/gocron"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
func SetupReportCronJob(reportWorker *worker.ReportWorker) {
|
||||
s := gocron.NewScheduler(time.UTC)
|
||||
|
||||
// Daily at midnight
|
||||
_, _ = s.Every(1).Day().At("00:00").Do(func() {
|
||||
_ = reportWorker.GenerateAndExport(domain.Daily)
|
||||
})
|
||||
|
||||
// Weekly on Sunday at 00:05
|
||||
_, _ = s.Every(1).Week().Sunday().At("00:05").Do(func() {
|
||||
_ = reportWorker.GenerateAndExport(domain.Weekly)
|
||||
})
|
||||
|
||||
// Monthly on 1st at 00:10
|
||||
_, _ = s.Every(1).Month(1).At("00:10").Do(func() {
|
||||
_ = reportWorker.GenerateAndExport(domain.Monthly)
|
||||
})
|
||||
|
||||
s.StartAsync()
|
||||
}
|
||||
|
||||
func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.Service, resultService *resultsvc.Service) {
|
||||
c := cron.New(cron.WithSeconds())
|
||||
|
||||
|
|
|
|||
29
internal/web_server/worker/report.go
Normal file
29
internal/web_server/worker/report.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// worker/report_worker.go
|
||||
package worker
|
||||
|
||||
import (
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/infrastructure"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/report"
|
||||
)
|
||||
|
||||
type ReportWorker struct {
|
||||
reportService *report.Service
|
||||
exporter infrastructure.CSVExporter
|
||||
}
|
||||
|
||||
func NewReportWorker(service *report.Service, exporter infrastructure.CSVExporter) *ReportWorker {
|
||||
return &ReportWorker{
|
||||
reportService: service,
|
||||
exporter: exporter,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ReportWorker) GenerateAndExport(timeFrame domain.TimeFrame) error {
|
||||
report, err := w.reportService.GenerateReport(timeFrame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.exporter.Export(report)
|
||||
}
|
||||
0
logs/app.log
Normal file
0
logs/app.log
Normal file
39033
logs/failed_markets.log
Normal file
39033
logs/failed_markets.log
Normal file
File diff suppressed because it is too large
Load Diff
1
tmp/build-errors.log
Normal file
1
tmp/build-errors.log
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user