- Introduced BetStatStore, BranchStatStore, and WalletStatStore interfaces for handling statistics. - Implemented repository methods for fetching and updating bet, branch, and wallet statistics. - Created reporting services for generating interval reports for bets, branches, companies, and wallets. - Enhanced CSV writing functionality to support dynamic struct to CSV conversion. - Added cron jobs for periodic updates of branch and wallet statistics. - Updated wallet handler to include transaction statistics in the response.
121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
package report
|
|
|
|
import (
|
|
"context"
|
|
"encoding/csv"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
"reflect"
|
|
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
|
|
var (
|
|
ErrReportFileNotFound = errors.New("failed to find report file")
|
|
ErrReportFileError = errors.New("unknown error with report file")
|
|
ErrReportNotComplete = errors.New("report is not completed")
|
|
ErrReportFilePathInvalid = errors.New("report file path is invalid")
|
|
)
|
|
|
|
func StructToCSVRow(v any) ([]string, []string) {
|
|
t := reflect.TypeOf(v)
|
|
val := reflect.ValueOf(v)
|
|
headers := make([]string, t.NumField())
|
|
row := make([]string, t.NumField())
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
field := t.Field(i)
|
|
headers[i] = field.Tag.Get("csv")
|
|
row[i] = fmt.Sprint(val.Field(i).Interface())
|
|
}
|
|
return headers, row
|
|
}
|
|
|
|
func (s *Service) WriteCSV(rows [][]string, filePrefix string) (string, error) {
|
|
if len(rows) == 0 {
|
|
s.mongoLogger.Error("[WriteCSV] CSV with no data",
|
|
zap.String("file_prefix", filePrefix),
|
|
)
|
|
return "", errors.New("no data to write")
|
|
}
|
|
|
|
filename := fmt.Sprintf("%s_%s_%s.csv",
|
|
filePrefix,
|
|
time.Now().Format("2006-01-02_15-04-05"),
|
|
uuid.NewString()[:8],
|
|
)
|
|
|
|
filePath := filepath.Join(s.cfg.ReportExportPath, filename)
|
|
|
|
file, err := os.Create(filePath)
|
|
if err != nil {
|
|
s.mongoLogger.Error("[WriteCSV] Failed to create file",
|
|
zap.String("file", filename),
|
|
zap.String("path", s.cfg.ReportExportPath),
|
|
zap.Error(err),
|
|
)
|
|
return "", fmt.Errorf("create csv: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
writer := csv.NewWriter(file)
|
|
if err := writer.WriteAll(rows); err != nil {
|
|
s.mongoLogger.Error("[WriteCSV] Error while writing csv",
|
|
zap.String("file", filename),
|
|
zap.String("path", s.cfg.ReportExportPath),
|
|
zap.Error(err),
|
|
)
|
|
return "", fmt.Errorf("write csv: %w", err)
|
|
}
|
|
|
|
return filePath, nil
|
|
}
|
|
|
|
func (s *Service) CheckAndFetchReportFile(ctx context.Context, ID int64) (string, error) {
|
|
report, err := s.GetReportRequestByID(ctx, ID)
|
|
if err != nil {
|
|
s.mongoLogger.Error("[CheckAndFetchReportFile] Failed to get report request by id",
|
|
zap.Error(err),
|
|
)
|
|
return "", fmt.Errorf("failed to get report request:%w", err)
|
|
}
|
|
|
|
if report.Status != domain.SuccessReportRequest {
|
|
s.mongoLogger.Error("[CheckAndFetchReportFile] Attempted download of report that isn't completed",
|
|
zap.String("status", string(report.Status)),
|
|
)
|
|
return "", ErrReportNotComplete
|
|
}
|
|
|
|
if !report.FilePath.Valid {
|
|
s.mongoLogger.Error("[CheckAndFetchReportFile] File Path is invalid even though the report is a success",
|
|
zap.String("file path", report.FilePath.Value),
|
|
)
|
|
return "", ErrReportFilePathInvalid
|
|
}
|
|
|
|
// Check if the report file exists
|
|
if _, err := os.Stat(report.FilePath.Value); err != nil {
|
|
if os.IsNotExist(err) {
|
|
s.mongoLogger.Error("[CheckAndFetchReportFile] Unable to find report file",
|
|
zap.String("file path", report.FilePath.Value),
|
|
)
|
|
return "", ErrReportFileNotFound
|
|
}
|
|
|
|
s.mongoLogger.Error("[CheckAndFetchReportFile] Unable to check report file",
|
|
zap.String("file path", report.FilePath.Value),
|
|
zap.Error(err),
|
|
)
|
|
return "", ErrReportFileError
|
|
}
|
|
|
|
return report.FilePath.Value, nil
|
|
}
|