diff --git a/internal/domain/bill/adapter.db.go b/internal/domain/bill/adapter.db.go index 32dc994..5f36794 100644 --- a/internal/domain/bill/adapter.db.go +++ b/internal/domain/bill/adapter.db.go @@ -1,3 +1,64 @@ +// package bill + +// import ( +// "github.com/esc-chula/intania-888-backend/internal/model" +// "gorm.io/gorm" +// ) + +// type billRepositoryImpl struct { +// db *gorm.DB +// } + +// // NewBillRepository creates a new BillRepository instance +// func NewBillRepository(db *gorm.DB) BillRepository { +// return &billRepositoryImpl{db} +// } + +// // Create a new bill +// func (r *billRepositoryImpl) Create(bill *model.BillHead) error { +// return r.db.Create(bill).Error +// } + +// // GetById retrieves a bill by its ID +// func (r *billRepositoryImpl) GetById(billId, userId string) (*model.BillHead, error) { +// var bill model.BillHead +// err := r.db.Preload("Lines").Preload("Lines.Match").Where("id = ? AND user_id = ?", billId, userId).First(&bill).Error +// if err != nil { +// return nil, err +// } +// return &bill, nil +// } + +// // GetAll retrieves all bills for a specific user +// func (r *billRepositoryImpl) GetAll(userId string) ([]*model.BillHead, error) { +// var bills []*model.BillHead +// err := r.db.Preload("Lines").Preload("Lines.Match").Where("user_id = ?", userId).Find(&bills).Error +// if err != nil { +// return nil, err +// } +// return bills, nil +// } + +// // GetAllAdmin retrieves all bills from all users (admin only) +// func (r *billRepositoryImpl) GetAllAdmin() ([]*model.BillHead, error) { +// var bills []*model.BillHead +// err := r.db.Preload("Lines").Preload("Lines.Match").Preload("User").Find(&bills).Error +// if err != nil { +// return nil, err +// } +// return bills, nil +// } + +// // Update an existing bill +// func (r *billRepositoryImpl) Update(bill *model.BillHead) error { +// return r.db.Model(bill).Where("id = ?", bill.Id).Updates(bill).Error +// } + +// // Delete a bill by its ID +// func (r *billRepositoryImpl) Delete(id string) error { +// return r.db.Delete(&model.BillHead{}, "id = ?", id).Error +// } + package bill import ( @@ -14,47 +75,125 @@ func NewBillRepository(db *gorm.DB) BillRepository { return &billRepositoryImpl{db} } -// Create a new bill +// Create a new bill (keep GORM for struct mapping) func (r *billRepositoryImpl) Create(bill *model.BillHead) error { return r.db.Create(bill).Error } -// GetById retrieves a bill by its ID +// GetById retrieves a bill by its ID and user ID func (r *billRepositoryImpl) GetById(billId, userId string) (*model.BillHead, error) { var bill model.BillHead - err := r.db.Preload("Lines").Preload("Lines.Match").Where("id = ? AND user_id = ?", billId, userId).First(&bill).Error + + // Use db.Raw for main query + err := r.db.Raw(` + SELECT * FROM bill_heads + WHERE id = $1 AND user_id = $2 + LIMIT 1 + `, billId, userId).Scan(&bill).Error if err != nil { return nil, err } + + // Preload Lines and Matches manually + var lines []model.BillLine + err = r.db.Raw(` + SELECT * FROM bill_lines + WHERE bill_id = $1 + `, bill.Id).Scan(&lines).Error + if err == nil { + for _, line := range lines { + var match model.Match + _ = r.db.Raw(`SELECT * FROM matches WHERE id = $1`, line.MatchId).Scan(&match).Error + line.Match = match + } + bill.Lines = lines + } + return &bill, nil } // GetAll retrieves all bills for a specific user func (r *billRepositoryImpl) GetAll(userId string) ([]*model.BillHead, error) { var bills []*model.BillHead - err := r.db.Preload("Lines").Preload("Lines.Match").Where("user_id = ?", userId).Find(&bills).Error + + err := r.db.Raw(` + SELECT * FROM bill_heads + WHERE user_id = $1 + ORDER BY created_at DESC + `, userId).Scan(&bills).Error if err != nil { return nil, err } + + // Preload Lines & Matches for each bill + for _, bill := range bills { + var lines []model.BillLine + _ = r.db.Raw(` + SELECT * FROM bill_lines + WHERE bill_id = $1 + `, bill.Id).Scan(&lines).Error + + for _, line := range lines { + var match model.Match + _ = r.db.Raw(`SELECT * FROM matches WHERE id = $1`, line.MatchId).Scan(&match).Error + line.Match = match + } + bill.Lines = lines + } + return bills, nil } -// GetAllAdmin retrieves all bills from all users (admin only) +// GetAllAdmin retrieves all bills (with users) func (r *billRepositoryImpl) GetAllAdmin() ([]*model.BillHead, error) { var bills []*model.BillHead - err := r.db.Preload("Lines").Preload("Lines.Match").Preload("User").Find(&bills).Error + + err := r.db.Raw(` + SELECT * FROM bill_heads + ORDER BY created_at DESC + `).Scan(&bills).Error if err != nil { return nil, err } + + // Preload Lines, Matches, and Users manually + for _, bill := range bills { + var lines []model.BillLine + _ = r.db.Raw(` + SELECT * FROM bill_lines + WHERE bill_id = $1 + `, bill.Id).Scan(&lines).Error + + for _, line := range lines { + var match model.Match + _ = r.db.Raw(`SELECT * FROM matches WHERE id = $1`, line.MatchId).Scan(&match).Error + line.Match = match + } + + bill.Lines = lines + + var user model.User + _ = r.db.Raw(`SELECT * FROM users WHERE id = $1`, bill.UserId).Scan(&user).Error + bill.User = user + } + return bills, nil } // Update an existing bill func (r *billRepositoryImpl) Update(bill *model.BillHead) error { - return r.db.Model(bill).Where("id = ?", bill.Id).Updates(bill).Error + return r.db.Exec(` + UPDATE bill_heads + SET total = $1, + updated_at = NOW() + WHERE id = $2 + `, bill.Total, bill.Id).Error } // Delete a bill by its ID func (r *billRepositoryImpl) Delete(id string) error { - return r.db.Delete(&model.BillHead{}, "id = ?", id).Error + return r.db.Exec(` + DELETE FROM bill_heads + WHERE id = $1 + `, id).Error } diff --git a/internal/domain/color/adapter.db.go b/internal/domain/color/adapter.db.go index f900d91..7972357 100644 --- a/internal/domain/color/adapter.db.go +++ b/internal/domain/color/adapter.db.go @@ -1,6 +1,101 @@ +// package color + +// import ( +// "github.com/esc-chula/intania-888-backend/internal/model" +// "gorm.io/gorm" +// ) + +// type colorRepository struct { +// db *gorm.DB +// } + +// func NewColorRepository(db *gorm.DB) ColorRepository { +// return &colorRepository{ +// db: db, +// } +// } + +// func (r *colorRepository) GetAllLeaderboards(typeId string) ([]*model.Color, error) { +// var colors []*model.Color + +// // Base query +// query := r.db.Table("colors"). +// Select(` +// colors.*, +// COUNT(matches.id) as total_matches, +// SUM(CASE WHEN matches.is_draw = TRUE THEN 1 ELSE 0 END) as drawn, +// SUM(CASE WHEN matches.winner_id = colors.id THEN 1 ELSE 0 END) as won +// `). +// Group("colors.id") + +// // Join +// matchJoin := ` +// LEFT JOIN matches +// ON (matches.winner_id IS NOT NULL OR matches.is_draw = TRUE) +// AND (colors.id = matches.teama_id OR colors.id = matches.teamb_id) +// ` +// if typeId != "" { +// matchJoin += " AND matches.type_id = ?" +// query = query.Joins(matchJoin, typeId) +// } else { +// query = query.Joins(matchJoin) +// } + +// // Execute +// if err := query.Find(&colors).Error; err != nil { +// return nil, err +// } + +// return colors, nil +// } + +// func (r *colorRepository) GetGroupStageTable(typeId, groupId string) ([]*model.Color, error) { +// var colors []*model.Color + +// // Base query +// query := r.db.Table("colors"). +// Select(` +// colors.*, +// COUNT(DISTINCT matches.id) as total_matches, +// COUNT(DISTINCT CASE WHEN matches.is_draw = TRUE THEN matches.id END) as drawn, +// COUNT(DISTINCT CASE WHEN matches.winner_id = colors.id THEN matches.id END) as won +// `). +// Group("colors.id") + +// // Join matches table +// matchJoin := ` +// LEFT JOIN matches +// ON (matches.winner_id IS NOT NULL OR matches.is_draw = TRUE) +// AND (colors.id = matches.teama_id OR colors.id = matches.teamb_id) +// ` +// if typeId != "" { +// matchJoin += " AND matches.type_id = ?" +// query = query.Joins(matchJoin, typeId) +// } else { +// query = query.Joins(matchJoin) +// } + +// // Join group_stages table to filter by groupId +// if groupId != "" { +// query = query.Joins(` +// INNER JOIN group_stages +// ON group_stages.color_id = colors.id +// AND group_stages.id = ?`, groupId) +// } + +// // Execute the query +// if err := query.Find(&colors).Error; err != nil { +// return nil, err +// } + +// return colors, nil +// } + package color import ( + "fmt" + "github.com/esc-chula/intania-888-backend/internal/model" "gorm.io/gorm" ) @@ -15,76 +110,76 @@ func NewColorRepository(db *gorm.DB) ColorRepository { } } +// GetAllLeaderboards retrieves leaderboard info for each color func (r *colorRepository) GetAllLeaderboards(typeId string) ([]*model.Color, error) { var colors []*model.Color - // Base query - query := r.db.Table("colors"). - Select(` - colors.*, - COUNT(matches.id) as total_matches, - SUM(CASE WHEN matches.is_draw = TRUE THEN 1 ELSE 0 END) as drawn, - SUM(CASE WHEN matches.winner_id = colors.id THEN 1 ELSE 0 END) as won - `). - Group("colors.id") - - // Join - matchJoin := ` - LEFT JOIN matches - ON (matches.winner_id IS NOT NULL OR matches.is_draw = TRUE) - AND (colors.id = matches.teama_id OR colors.id = matches.teamb_id) + baseSQL := ` + SELECT + colors.*, + COUNT(matches.id) AS total_matches, + SUM(CASE WHEN matches.is_draw = TRUE THEN 1 ELSE 0 END) AS drawn, + SUM(CASE WHEN matches.winner_id = colors.id THEN 1 ELSE 0 END) AS won + FROM colors + LEFT JOIN matches + ON (matches.winner_id IS NOT NULL OR matches.is_draw = TRUE) + AND (colors.id = matches.teama_id OR colors.id = matches.teamb_id) ` + args := []interface{}{} + if typeId != "" { - matchJoin += " AND matches.type_id = ?" - query = query.Joins(matchJoin, typeId) - } else { - query = query.Joins(matchJoin) + baseSQL += " AND matches.type_id = $1" + args = append(args, typeId) } - // Execute - if err := query.Find(&colors).Error; err != nil { + baseSQL += " GROUP BY colors.id" + + err := r.db.Raw(baseSQL, args...).Scan(&colors).Error + if err != nil { return nil, err } return colors, nil } +// GetGroupStageTable retrieves leaderboard info for a specific group stage func (r *colorRepository) GetGroupStageTable(typeId, groupId string) ([]*model.Color, error) { var colors []*model.Color - // Base query - query := r.db.Table("colors"). - Select(` - colors.*, - COUNT(DISTINCT matches.id) as total_matches, - COUNT(DISTINCT CASE WHEN matches.is_draw = TRUE THEN matches.id END) as drawn, - COUNT(DISTINCT CASE WHEN matches.winner_id = colors.id THEN matches.id END) as won - `). - Group("colors.id") - - // Join matches table - matchJoin := ` - LEFT JOIN matches - ON (matches.winner_id IS NOT NULL OR matches.is_draw = TRUE) - AND (colors.id = matches.teama_id OR colors.id = matches.teamb_id) + baseSQL := ` + SELECT + colors.*, + COUNT(DISTINCT matches.id) AS total_matches, + COUNT(DISTINCT CASE WHEN matches.is_draw = TRUE THEN matches.id END) AS drawn, + COUNT(DISTINCT CASE WHEN matches.winner_id = colors.id THEN matches.id END) AS won + FROM colors + LEFT JOIN matches + ON (matches.winner_id IS NOT NULL OR matches.is_draw = TRUE) + AND (colors.id = matches.teama_id OR colors.id = matches.teamb_id) ` + args := []interface{}{} + argIndex := 1 + if typeId != "" { - matchJoin += " AND matches.type_id = ?" - query = query.Joins(matchJoin, typeId) - } else { - query = query.Joins(matchJoin) + baseSQL += fmt.Sprintf(" AND matches.type_id = $%d", argIndex) + args = append(args, typeId) + argIndex++ } - // Join group_stages table to filter by groupId if groupId != "" { - query = query.Joins(` + baseSQL += fmt.Sprintf(` INNER JOIN group_stages - ON group_stages.color_id = colors.id - AND group_stages.id = ?`, groupId) + ON group_stages.color_id = colors.id + AND group_stages.id = $%d + `, argIndex) + args = append(args, groupId) + argIndex++ } - // Execute the query - if err := query.Find(&colors).Error; err != nil { + baseSQL += " GROUP BY colors.id" + + err := r.db.Raw(baseSQL, args...).Scan(&colors).Error + if err != nil { return nil, err } diff --git a/internal/domain/match/adapter.db.go b/internal/domain/match/adapter.db.go index 7ba5cbf..862146f 100644 --- a/internal/domain/match/adapter.db.go +++ b/internal/domain/match/adapter.db.go @@ -1,6 +1,124 @@ +// package match + +// import ( +// "time" + +// "github.com/esc-chula/intania-888-backend/internal/model" +// "gorm.io/gorm" +// ) + +// type matchRepositoryImpl struct { +// db *gorm.DB +// } + +// func NewMatchRepository(db *gorm.DB) MatchRepository { +// return &matchRepositoryImpl{db} +// } + +// func (r *matchRepositoryImpl) Create(match *model.Match) error { + +// return r.db.Create(match).Error +// } + +// func (r *matchRepositoryImpl) GetById(matchId string) (*model.Match, error) { +// var match model.Match +// err := r.db.Where("id = ?", matchId).First(&match).Error +// if err != nil { +// return nil, err +// } +// return &match, nil +// } + +// func (r *matchRepositoryImpl) GetAll(filter *model.MatchFilter) ([]*model.Match, error) { +// var matches []*model.Match +// db := r.db + +// if filter != nil { +// if filter.TypeId != "" { +// db = db.Where("type_id = ?", filter.TypeId) +// } + +// now := time.Now() + +// switch filter.Schedule { +// case model.Schedule: +// db = db.Where("end_time > ?", now) +// case model.Result: +// db = db.Where("end_time <= ?", now) +// } +// } + +// err := db.Order("start_time").Find(&matches).Error +// if err != nil { +// return nil, err +// } +// return matches, nil +// } + +// func (r *matchRepositoryImpl) CountBetsForTeam(matchId string, teamId string) (int64, error) { +// var count int64 +// err := r.db.Model(&model.BillLine{}).Where("match_id = ? AND betting_on = ?", matchId, teamId).Count(&count).Error +// if err != nil { +// return 0, err +// } +// return count, nil +// } + +// func (r *matchRepositoryImpl) UpdateScore(match *model.Match) error { +// return r.db.Model(&model.Match{}). +// Where("id = ?", match.Id). +// Updates(map[string]interface{}{ +// "teama_score": match.TeamA_Score, +// "teamb_score": match.TeamB_Score, +// }).Error +// } + +// func (r *matchRepositoryImpl) UpdateWinner(match *model.Match) error { +// return r.db.Model(&model.Match{}). +// Where("id = ?", match.Id). +// Update("winner_id", match.WinnerId).Error +// } + +// func (r *matchRepositoryImpl) Delete(id string) error { +// return r.db.Delete(&model.Match{}, "id = ?", id).Error +// } + +// func (r *matchRepositoryImpl) GetBillHeadsForMatch(matchId string) ([]*model.BillHead, error) { +// var billHeads []*model.BillHead +// err := r.db.Table("bill_heads").Preload("Lines").Preload("Lines.Match"). +// Joins("JOIN bill_lines ON bill_heads.id = bill_lines.bill_id"). +// Where("bill_lines.match_id = ?", matchId). +// Find(&billHeads).Error +// if err != nil { +// return nil, err +// } +// return billHeads, nil +// } + +// func (r *matchRepositoryImpl) PayoutToUser(userId string, amount float64) error { +// return r.db.Model(&model.User{}).Where("id = ?", userId). +// Update("remaining_coin", gorm.Expr("remaining_coin + ?", amount)).Error +// } + +// func (r *matchRepositoryImpl) MarkBillLineAsPaid(billId string, matchId string) error { +// return r.db.Model(&model.BillLine{}). +// Where("bill_id = ? AND match_id = ?", billId, matchId). +// Update("is_paid", true).Error +// } + +// func (r *matchRepositoryImpl) UpdateMatch(match *model.Match) error { +// return r.db.Model(&model.Match{}). +// Where("id = ?", match.Id). +// Updates(map[string]interface{}{ +// "is_draw": match.IsDraw, +// "updated_at": time.Now(), +// }).Error +// } + package match import ( + "fmt" "time" "github.com/esc-chula/intania-888-backend/internal/model" @@ -21,33 +139,59 @@ func (r *matchRepositoryImpl) Create(match *model.Match) error { func (r *matchRepositoryImpl) GetById(matchId string) (*model.Match, error) { var match model.Match - err := r.db.Where("id = ?", matchId).First(&match).Error + err := r.db.Raw(` + SELECT * FROM matches + WHERE id = $1 + LIMIT 1 + `, matchId).Scan(&match).Error if err != nil { return nil, err } + + if match.Id == "" { + return nil, gorm.ErrRecordNotFound + } + return &match, nil } func (r *matchRepositoryImpl) GetAll(filter *model.MatchFilter) ([]*model.Match, error) { var matches []*model.Match - db := r.db + + baseQuery := ` + SELECT * FROM matches + ` + whereClauses := []string{} + args := []interface{}{} + argIndex := 1 if filter != nil { if filter.TypeId != "" { - db = db.Where("type_id = ?", filter.TypeId) + whereClauses = append(whereClauses, `type_id = $`+itoa(argIndex)) + args = append(args, filter.TypeId) + argIndex++ } now := time.Now() - switch filter.Schedule { case model.Schedule: - db = db.Where("end_time > ?", now) + whereClauses = append(whereClauses, `end_time > $`+itoa(argIndex)) + args = append(args, now) + argIndex++ case model.Result: - db = db.Where("end_time <= ?", now) + whereClauses = append(whereClauses, `end_time <= $`+itoa(argIndex)) + args = append(args, now) + argIndex++ } } - err := db.Order("start_time").Find(&matches).Error + if len(whereClauses) > 0 { + baseQuery += " WHERE " + joinClauses(whereClauses, " AND ") + } + + baseQuery += " ORDER BY start_time" + + err := r.db.Raw(baseQuery, args...).Scan(&matches).Error if err != nil { return nil, err } @@ -56,7 +200,10 @@ func (r *matchRepositoryImpl) GetAll(filter *model.MatchFilter) ([]*model.Match, func (r *matchRepositoryImpl) CountBetsForTeam(matchId string, teamId string) (int64, error) { var count int64 - err := r.db.Model(&model.BillLine{}).Where("match_id = ? AND betting_on = ?", matchId, teamId).Count(&count).Error + err := r.db.Raw(` + SELECT COUNT(*) FROM bill_lines + WHERE match_id = $1 AND betting_on = $2 + `, matchId, teamId).Scan(&count).Error if err != nil { return 0, err } @@ -64,52 +211,89 @@ func (r *matchRepositoryImpl) CountBetsForTeam(matchId string, teamId string) (i } func (r *matchRepositoryImpl) UpdateScore(match *model.Match) error { - return r.db.Model(&model.Match{}). - Where("id = ?", match.Id). - Updates(map[string]interface{}{ - "teama_score": match.TeamA_Score, - "teamb_score": match.TeamB_Score, - }).Error + return r.db.Exec(` + UPDATE matches + SET teama_score = $1, teamb_score = $2 + WHERE id = $3 + `, match.TeamA_Score, match.TeamB_Score, match.Id).Error } func (r *matchRepositoryImpl) UpdateWinner(match *model.Match) error { - return r.db.Model(&model.Match{}). - Where("id = ?", match.Id). - Update("winner_id", match.WinnerId).Error + return r.db.Exec(` + UPDATE matches + SET winner_id = $1 + WHERE id = $2 + `, match.WinnerId, match.Id).Error } func (r *matchRepositoryImpl) Delete(id string) error { - return r.db.Delete(&model.Match{}, "id = ?", id).Error + return r.db.Exec(` + DELETE FROM matches + WHERE id = $1 + `, id).Error } func (r *matchRepositoryImpl) GetBillHeadsForMatch(matchId string) ([]*model.BillHead, error) { var billHeads []*model.BillHead - err := r.db.Table("bill_heads").Preload("Lines").Preload("Lines.Match"). - Joins("JOIN bill_lines ON bill_heads.id = bill_lines.bill_id"). - Where("bill_lines.match_id = ?", matchId). - Find(&billHeads).Error + err := r.db.Raw(` + SELECT bh.* + FROM bill_heads bh + JOIN bill_lines bl ON bh.id = bl.bill_id + WHERE bl.match_id = $1 + `, matchId).Scan(&billHeads).Error if err != nil { return nil, err } + + // Optional: preload lines if needed + for _, bh := range billHeads { + var lines []model.BillLine + _ = r.db.Raw(` + SELECT * FROM bill_lines + WHERE bill_id = $1 + `, bh.Id).Scan(&lines).Error + bh.Lines = lines + } return billHeads, nil } func (r *matchRepositoryImpl) PayoutToUser(userId string, amount float64) error { - return r.db.Model(&model.User{}).Where("id = ?", userId). - Update("remaining_coin", gorm.Expr("remaining_coin + ?", amount)).Error + return r.db.Exec(` + UPDATE users + SET remaining_coin = remaining_coin + $1 + WHERE id = $2 + `, amount, userId).Error } func (r *matchRepositoryImpl) MarkBillLineAsPaid(billId string, matchId string) error { - return r.db.Model(&model.BillLine{}). - Where("bill_id = ? AND match_id = ?", billId, matchId). - Update("is_paid", true).Error + return r.db.Exec(` + UPDATE bill_lines + SET is_paid = TRUE + WHERE bill_id = $1 AND match_id = $2 + `, billId, matchId).Error } func (r *matchRepositoryImpl) UpdateMatch(match *model.Match) error { - return r.db.Model(&model.Match{}). - Where("id = ?", match.Id). - Updates(map[string]interface{}{ - "is_draw": match.IsDraw, - "updated_at": time.Now(), - }).Error + return r.db.Exec(` + UPDATE matches + SET is_draw = $1, updated_at = $2 + WHERE id = $3 + `, match.IsDraw, time.Now(), match.Id).Error +} + +// --- Helper functions --- + +func itoa(i int) string { + return fmt.Sprintf("%d", i) +} + +func joinClauses(clauses []string, sep string) string { + out := "" + for i, c := range clauses { + if i > 0 { + out += sep + } + out += c + } + return out } diff --git a/internal/domain/match/service.go b/internal/domain/match/service.go index 4bd13f4..10fda0f 100644 --- a/internal/domain/match/service.go +++ b/internal/domain/match/service.go @@ -7,6 +7,7 @@ import ( "github.com/esc-chula/intania-888-backend/internal/model" "github.com/google/uuid" "go.uber.org/zap" + "gorm.io/gorm" ) type matchServiceImpl struct { @@ -37,8 +38,17 @@ func (s *matchServiceImpl) CreateMatch(matchDto *model.MatchDto) error { func (s *matchServiceImpl) GetMatch(matchId string) (*model.MatchDto, error) { match, err := s.repo.GetById(matchId) + // if err != nil { + // s.log.Named("GetMatch").Error("GetById", zap.Error(err)) + // return nil, err + // } + if err != nil { - s.log.Named("GetMatch").Error("GetById", zap.Error(err)) + if errors.Is(err, gorm.ErrRecordNotFound) { + s.log.Named("GetMatch").Warn("Match not found", zap.String("id", matchId)) + return nil, errors.New("match not found") + } + s.log.Named("GetMatch").Error("GetById failed", zap.Error(err)) return nil, err } diff --git a/internal/domain/sporttype/adapter.db.go b/internal/domain/sporttype/adapter.db.go index b5239de..723eb1f 100644 --- a/internal/domain/sporttype/adapter.db.go +++ b/internal/domain/sporttype/adapter.db.go @@ -1,3 +1,30 @@ +// package sporttype + +// import ( +// "github.com/esc-chula/intania-888-backend/internal/model" +// "gorm.io/gorm" +// ) + +// type sportTypeRepository struct { +// db *gorm.DB +// } + +// func NewSportTypeRepository(db *gorm.DB) SportTypeRepository { +// return &sportTypeRepository{ +// db: db, +// } +// } + +// func (r *sportTypeRepository) GetAllSportTypes() ([]*model.SportType, error) { +// var sportTypes []*model.SportType + +// if err := r.db.Find(&sportTypes).Error; err != nil { +// return nil, err +// } + +// return sportTypes, nil +// } + package sporttype import ( @@ -10,15 +37,18 @@ type sportTypeRepository struct { } func NewSportTypeRepository(db *gorm.DB) SportTypeRepository { - return &sportTypeRepository{ - db: db, - } + return &sportTypeRepository{db: db} } +// GetAllSportTypes retrieves all sport types (PostgreSQL style) func (r *sportTypeRepository) GetAllSportTypes() ([]*model.SportType, error) { var sportTypes []*model.SportType - if err := r.db.Find(&sportTypes).Error; err != nil { + err := r.db.Raw(` + SELECT * FROM sport_types + ORDER BY id ASC + `).Scan(&sportTypes).Error + if err != nil { return nil, err } diff --git a/internal/domain/stakemine/adapter.db.go b/internal/domain/stakemine/adapter.db.go index 93da4dc..6630945 100644 --- a/internal/domain/stakemine/adapter.db.go +++ b/internal/domain/stakemine/adapter.db.go @@ -1,4 +1,98 @@ -// internal/domain/stakemine/adapter.db.go +// // internal/domain/stakemine/adapter.db.go +// package stakemine + +// import ( +// "github.com/esc-chula/intania-888-backend/internal/model" +// "gorm.io/gorm" +// ) + +// type stakeMineRepositoryImpl struct { +// db *gorm.DB +// } + +// func NewStakeMineRepository(db *gorm.DB) StakeMineRepository { +// return &stakeMineRepositoryImpl{db: db} +// } + +// func (r *stakeMineRepositoryImpl) Create(game *model.MineGame) error { +// return r.db.Create(game).Error +// } + +// func (r *stakeMineRepositoryImpl) Update(game *model.MineGame) error { +// return r.db.Save(game).Error +// } + +// func (r *stakeMineRepositoryImpl) FindById(gameId string) (*model.MineGame, error) { +// var game model.MineGame +// err := r.db.Where("id = ?", gameId).First(&game).Error +// if err != nil { +// return nil, err +// } +// return &game, nil +// } + +// func (r *stakeMineRepositoryImpl) FindActiveByUserId(userId string) (*model.MineGame, error) { +// var game model.MineGame +// err := r.db.Where("user_id = ? AND status = ?", userId, "active").First(&game).Error +// if err != nil { +// return nil, err +// } +// return &game, nil +// } + +// func (r *stakeMineRepositoryImpl) FindByUserId(userId string, limit int, offset int) ([]model.MineGame, error) { +// var games []model.MineGame +// err := r.db.Where("user_id = ?", userId). +// Order("created_at DESC"). +// Limit(limit). +// Offset(offset). +// Find(&games).Error +// if err != nil { +// return nil, err +// } +// return games, nil +// } + +// func (r *stakeMineRepositoryImpl) CreateHistory(history *model.MineGameHistory) error { +// return r.db.Create(history).Error +// } + +// func (r *stakeMineRepositoryImpl) GetStatsByUserId(userId string) (*model.MineGameStatsDto, error) { +// var stats model.MineGameStatsDto + +// // Count games by status +// var gamesWon, gamesLost, gamesCashedOut int64 +// r.db.Model(&model.MineGame{}).Where("user_id = ? AND status = ?", userId, "won").Count(&gamesWon) +// r.db.Model(&model.MineGame{}).Where("user_id = ? AND status = ?", userId, "lost").Count(&gamesLost) +// r.db.Model(&model.MineGame{}).Where("user_id = ? AND status = ?", userId, "cashed_out").Count(&gamesCashedOut) + +// stats.GamesWon = int(gamesWon) +// stats.GamesLost = int(gamesLost) +// stats.GamesCashedOut = int(gamesCashedOut) +// stats.TotalGames = stats.GamesWon + stats.GamesLost + stats.GamesCashedOut + +// // Calculate total wagered +// r.db.Model(&model.MineGame{}). +// Where("user_id = ?", userId). +// Select("COALESCE(SUM(bet_amount), 0)"). +// Scan(&stats.TotalWagered) + +// // Calculate total winnings (won + cashed out games only) +// r.db.Model(&model.MineGame{}). +// Where("user_id = ? AND status IN ?", userId, []string{"won", "cashed_out"}). +// Select("COALESCE(SUM(current_payout), 0)"). +// Scan(&stats.TotalWinnings) + +// stats.NetProfit = stats.TotalWinnings - stats.TotalWagered + +// // Calculate win rate +// if stats.TotalGames > 0 { +// stats.WinRate = float64(stats.GamesWon+stats.GamesCashedOut) / float64(stats.TotalGames) * 100 +// } + +// return &stats, nil +// } + package stakemine import ( @@ -15,17 +109,45 @@ func NewStakeMineRepository(db *gorm.DB) StakeMineRepository { } func (r *stakeMineRepositoryImpl) Create(game *model.MineGame) error { - return r.db.Create(game).Error + sql := ` + INSERT INTO mine_games ( + id, user_id, bet_amount, risk_level, status, + revealed_count, current_payout, multiplier, grid_data, + created_at, updated_at, completed_at + ) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW(), NOW(), $10) + ` + return r.db.Exec(sql, + game.Id, game.UserId, game.BetAmount, game.RiskLevel, game.Status, + game.RevealedCount, game.CurrentPayout, game.Multiplier, game.GridData, game.CompletedAt, + ).Error } func (r *stakeMineRepositoryImpl) Update(game *model.MineGame) error { - return r.db.Save(game).Error + sql := ` + UPDATE mine_games + SET user_id = $2, + bet_amount = $3, + risk_level = $4, + status = $5, + revealed_count = $6, + current_payout = $7, + multiplier = $8, + grid_data = $9, + completed_at = $10, + updated_at = NOW() + WHERE id = $1 + ` + return r.db.Exec(sql, + game.Id, game.UserId, game.BetAmount, game.RiskLevel, game.Status, + game.RevealedCount, game.CurrentPayout, game.Multiplier, game.GridData, game.CompletedAt, + ).Error } func (r *stakeMineRepositoryImpl) FindById(gameId string) (*model.MineGame, error) { var game model.MineGame - err := r.db.Where("id = ?", gameId).First(&game).Error - if err != nil { + sql := `SELECT * FROM mine_games WHERE id = $1 LIMIT 1` + if err := r.db.Raw(sql, gameId).Scan(&game).Error; err != nil { return nil, err } return &game, nil @@ -33,59 +155,74 @@ func (r *stakeMineRepositoryImpl) FindById(gameId string) (*model.MineGame, erro func (r *stakeMineRepositoryImpl) FindActiveByUserId(userId string) (*model.MineGame, error) { var game model.MineGame - err := r.db.Where("user_id = ? AND status = ?", userId, "active").First(&game).Error - if err != nil { - return nil, err + sql := ` + SELECT * FROM mine_games + WHERE user_id = $1 AND status = 'active' + LIMIT 1 + ` + result := r.db.Raw(sql, userId).Scan(&game) + + if result.Error != nil { + return nil, result.Error } + + if result.RowsAffected == 0 { + return nil, nil + } + return &game, nil } func (r *stakeMineRepositoryImpl) FindByUserId(userId string, limit int, offset int) ([]model.MineGame, error) { var games []model.MineGame - err := r.db.Where("user_id = ?", userId). - Order("created_at DESC"). - Limit(limit). - Offset(offset). - Find(&games).Error - if err != nil { + sql := ` + SELECT * FROM mine_games + WHERE user_id = $1 + ORDER BY created_at DESC + LIMIT $2 OFFSET $3 + ` + if err := r.db.Raw(sql, userId, limit, offset).Scan(&games).Error; err != nil { return nil, err } return games, nil } func (r *stakeMineRepositoryImpl) CreateHistory(history *model.MineGameHistory) error { - return r.db.Create(history).Error + sql := ` + INSERT INTO mine_game_histories ( + id, game_id, tile_index, tile_type, multiplier, payout_at_hit, created_at + ) + VALUES ($1, $2, $3, $4, $5, $6, NOW()) + ` + return r.db.Exec(sql, + history.Id, history.GameId, history.TileIndex, + history.TileType, history.Multiplier, history.PayoutAtHit, + ).Error } func (r *stakeMineRepositoryImpl) GetStatsByUserId(userId string) (*model.MineGameStatsDto, error) { var stats model.MineGameStatsDto - // Count games by status - var gamesWon, gamesLost, gamesCashedOut int64 - r.db.Model(&model.MineGame{}).Where("user_id = ? AND status = ?", userId, "won").Count(&gamesWon) - r.db.Model(&model.MineGame{}).Where("user_id = ? AND status = ?", userId, "lost").Count(&gamesLost) - r.db.Model(&model.MineGame{}).Where("user_id = ? AND status = ?", userId, "cashed_out").Count(&gamesCashedOut) + // Games by status + r.db.Raw(`SELECT COUNT(*) FROM mine_games WHERE user_id = $1 AND status = 'won'`, userId).Scan(&stats.GamesWon) + r.db.Raw(`SELECT COUNT(*) FROM mine_games WHERE user_id = $1 AND status = 'lost'`, userId).Scan(&stats.GamesLost) + r.db.Raw(`SELECT COUNT(*) FROM mine_games WHERE user_id = $1 AND status = 'cashed_out'`, userId).Scan(&stats.GamesCashedOut) - stats.GamesWon = int(gamesWon) - stats.GamesLost = int(gamesLost) - stats.GamesCashedOut = int(gamesCashedOut) stats.TotalGames = stats.GamesWon + stats.GamesLost + stats.GamesCashedOut - // Calculate total wagered - r.db.Model(&model.MineGame{}). - Where("user_id = ?", userId). - Select("COALESCE(SUM(bet_amount), 0)"). - Scan(&stats.TotalWagered) + // Total wagered + r.db.Raw(`SELECT COALESCE(SUM(bet_amount), 0) FROM mine_games WHERE user_id = $1`, userId).Scan(&stats.TotalWagered) - // Calculate total winnings (won + cashed out games only) - r.db.Model(&model.MineGame{}). - Where("user_id = ? AND status IN ?", userId, []string{"won", "cashed_out"}). - Select("COALESCE(SUM(current_payout), 0)"). - Scan(&stats.TotalWinnings) + // Total winnings (won + cashed_out) + r.db.Raw(` + SELECT COALESCE(SUM(current_payout), 0) + FROM mine_games + WHERE user_id = $1 AND status IN ('won', 'cashed_out') + `, userId).Scan(&stats.TotalWinnings) stats.NetProfit = stats.TotalWinnings - stats.TotalWagered - // Calculate win rate + // Win rate if stats.TotalGames > 0 { stats.WinRate = float64(stats.GamesWon+stats.GamesCashedOut) / float64(stats.TotalGames) * 100 } diff --git a/internal/domain/stakemine/service.go b/internal/domain/stakemine/service.go index 1fa4f1b..8530604 100644 --- a/internal/domain/stakemine/service.go +++ b/internal/domain/stakemine/service.go @@ -356,6 +356,10 @@ func (s *stakeMineServiceImpl) GetGame(userId string, gameId string) (*model.Min func (s *stakeMineServiceImpl) GetActiveGame(userId string) (*model.MineGameDto, error) { game, err := s.repo.FindActiveByUserId(userId) if err != nil { + return nil, err + } + + if game == nil { return nil, errors.New("no active game found") } diff --git a/internal/domain/user/adapter.db.go b/internal/domain/user/adapter.db.go index e1a8119..77aea80 100644 --- a/internal/domain/user/adapter.db.go +++ b/internal/domain/user/adapter.db.go @@ -1,6 +1,55 @@ +// package user + +// import ( +// "github.com/esc-chula/intania-888-backend/internal/model" +// "gorm.io/gorm" +// ) + +// type userRepositoryImpl struct { +// db *gorm.DB +// } + +// func NewUserRepository(db *gorm.DB) UserRepository { +// return &userRepositoryImpl{db: db} +// } + +// func (r *userRepositoryImpl) Create(user *model.User) error { +// return r.db.Create(user).Error +// } + +// func (r *userRepositoryImpl) GetById(id string) (*model.User, error) { +// var user model.User +// if err := r.db.Preload("Role").Where("id = ?", id).First(&user).Error; err != nil { +// return nil, err +// } +// return &user, nil +// } + +// func (r *userRepositoryImpl) GetByEmail(email string) (*model.User, error) { +// var user model.User +// if err := r.db.Preload("Role").Where("email = ?", email).First(&user).Error; err != nil { +// return nil, err +// } +// return &user, nil +// } + +// func (r *userRepositoryImpl) GetAll() ([]*model.User, error) { +// var users []*model.User +// if err := r.db.Preload("Role").Find(&users).Error; err != nil { +// return nil, err +// } +// return users, nil +// } + +// func (r *userRepositoryImpl) Update(user *model.User) error { +// return r.db.Model(user).Where("id = ?", user.Id).Updates(user).Error +// } + package user import ( + "errors" + "github.com/esc-chula/intania-888-backend/internal/model" "gorm.io/gorm" ) @@ -13,34 +62,123 @@ func NewUserRepository(db *gorm.DB) UserRepository { return &userRepositoryImpl{db: db} } +// Create inserts a new user func (r *userRepositoryImpl) Create(user *model.User) error { - return r.db.Create(user).Error + sql := ` + INSERT INTO users ( + id, email, name, nick_name, role_id, group_id, remaining_coin, created_at, updated_at + ) + VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW()) + ` + return r.db.Exec(sql, + user.Id, user.Email, user.Name, user.NickName, + user.RoleId, user.GroupId, user.RemainingCoin, + ).Error } +// GetById retrieves user + role + group func (r *userRepositoryImpl) GetById(id string) (*model.User, error) { var user model.User - if err := r.db.Preload("Role").Where("id = ?", id).First(&user).Error; err != nil { + + sql := ` + SELECT + u.id, u.email, u.name, u.nick_name, u.role_id, u.group_id, + u.remaining_coin, u.created_at, u.updated_at, + r.id AS "role.id", r.created_at AS "role.created_at", r.updated_at AS "role.updated_at", + g.id AS "group.id", g.color_id AS "group.color_id", + g.created_at AS "group.created_at", g.updated_at AS "group.updated_at" + FROM users u + LEFT JOIN roles r ON r.id = u.role_id + LEFT JOIN intania_groups g ON g.id = u.group_id + WHERE u.id = $1 + LIMIT 1 + ` + + if err := r.db.Raw(sql, id).Scan(&user).Error; err != nil { return nil, err } + if user.Id == "" { + return nil, errors.New("user not found") + } return &user, nil } +// GetByEmail retrieves user by email + role + group func (r *userRepositoryImpl) GetByEmail(email string) (*model.User, error) { var user model.User - if err := r.db.Preload("Role").Where("email = ?", email).First(&user).Error; err != nil { + + sql := ` + SELECT + u.id, u.email, u.name, u.nick_name, u.role_id, u.group_id, + u.remaining_coin, u.created_at, u.updated_at, + r.id AS "role.id", r.created_at AS "role.created_at", r.updated_at AS "role.updated_at", + g.id AS "group.id", g.color_id AS "group.color_id", + g.created_at AS "group.created_at", g.updated_at AS "group.updated_at" + FROM users u + LEFT JOIN roles r ON r.id = u.role_id + LEFT JOIN intania_groups g ON g.id = u.group_id + WHERE u.email = $1 + LIMIT 1 + ` + + if err := r.db.Raw(sql, email).Scan(&user).Error; err != nil { return nil, err } + if user.Id == "" { + return nil, errors.New("user not found") + } return &user, nil } +// GetAll retrieves all users with their roles and groups func (r *userRepositoryImpl) GetAll() ([]*model.User, error) { var users []*model.User - if err := r.db.Preload("Role").Find(&users).Error; err != nil { + + sql := ` + SELECT + u.id, u.email, u.name, u.nick_name, u.role_id, u.group_id, + u.remaining_coin, u.created_at, u.updated_at, + r.id AS "role.id", r.created_at AS "role.created_at", r.updated_at AS "role.updated_at", + g.id AS "group.id", g.color_id AS "group.color_id", + g.created_at AS "group.created_at", g.updated_at AS "group.updated_at" + FROM users u + LEFT JOIN roles r ON r.id = u.role_id + LEFT JOIN intania_groups g ON g.id = u.group_id + ORDER BY u.created_at DESC + ` + + if err := r.db.Raw(sql).Scan(&users).Error; err != nil { return nil, err } + return users, nil } +// Update modifies existing user info func (r *userRepositoryImpl) Update(user *model.User) error { - return r.db.Model(user).Where("id = ?", user.Id).Updates(user).Error + sql := ` + UPDATE users + SET + email = $1, + name = $2, + nick_name = $3, + role_id = $4, + group_id = $5, + remaining_coin = $6, + updated_at = NOW() + WHERE id = $7 + ` + result := r.db.Exec(sql, + user.Email, user.Name, user.NickName, + user.RoleId, user.GroupId, user.RemainingCoin, + user.Id, + ) + + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return errors.New("user not found") + } + return nil }