apskel-pos-backend/internal/repository/voucher_repository.go

207 lines
5.5 KiB
Go
Raw Normal View History

2025-09-13 15:37:26 +07:00
package repository
import (
"apskel-pos-be/internal/entities"
"context"
"math/rand"
"time"
"gorm.io/gorm"
)
type VoucherRepository struct {
db *gorm.DB
}
func NewVoucherRepository(db *gorm.DB) *VoucherRepository {
return &VoucherRepository{db: db}
}
func (r *VoucherRepository) Create(ctx context.Context, voucher *entities.Voucher) error {
return r.db.WithContext(ctx).Create(voucher).Error
}
func (r *VoucherRepository) GetByID(ctx context.Context, id int64) (*entities.Voucher, error) {
var voucher entities.Voucher
err := r.db.WithContext(ctx).Where("id = ?", id).First(&voucher).Error
if err != nil {
return nil, err
}
return &voucher, nil
}
func (r *VoucherRepository) GetByVoucherCode(ctx context.Context, voucherCode string) (*entities.Voucher, error) {
var voucher entities.Voucher
err := r.db.WithContext(ctx).Where("voucher_code = ?", voucherCode).First(&voucher).Error
if err != nil {
return nil, err
}
return &voucher, nil
}
func (r *VoucherRepository) List(ctx context.Context, offset, limit int) ([]entities.Voucher, int64, error) {
var vouchers []entities.Voucher
var total int64
query := r.db.WithContext(ctx)
if err := query.Model(&entities.Voucher{}).Count(&total).Error; err != nil {
return nil, 0, err
}
err := query.Offset(offset).Limit(limit).Find(&vouchers).Error
if err != nil {
return nil, 0, err
}
return vouchers, total, nil
}
func (r *VoucherRepository) GetRandomVouchers(ctx context.Context, limit int) ([]entities.Voucher, error) {
var vouchers []entities.Voucher
// First, get the total count
var total int64
if err := r.db.WithContext(ctx).Model(&entities.Voucher{}).Count(&total).Error; err != nil {
return nil, err
}
if total == 0 {
return vouchers, nil
}
// If we have fewer vouchers than requested, return all
if int(total) <= limit {
err := r.db.WithContext(ctx).Find(&vouchers).Error
return vouchers, err
}
// Generate random offsets to get random vouchers
rand.Seed(time.Now().UnixNano())
usedOffsets := make(map[int]bool)
for len(vouchers) < limit && len(usedOffsets) < int(total) {
offset := rand.Intn(int(total))
if usedOffsets[offset] {
continue
}
usedOffsets[offset] = true
var voucher entities.Voucher
err := r.db.WithContext(ctx).Offset(offset).Limit(1).First(&voucher).Error
if err != nil {
continue
}
vouchers = append(vouchers, voucher)
}
return vouchers, nil
}
func (r *VoucherRepository) GetRandomVouchersByRows(ctx context.Context, rows int, winnerNumber *int) ([][]entities.Voucher, error) {
var allVouchers []entities.Voucher
var err error
2025-09-13 17:34:34 +07:00
// Get all non-winner vouchers (regardless of winner_number)
nonWinnerQuery := r.db.WithContext(ctx).Where("is_winner = ?", false)
err = nonWinnerQuery.Find(&allVouchers).Error
if err != nil {
return nil, err
}
// If winner_number is provided, also include vouchers with that specific winner_number
// (even if they're already winners, to have a pool to select from)
2025-09-13 15:37:26 +07:00
if winnerNumber != nil {
2025-09-13 17:34:34 +07:00
var winnerNumberVouchers []entities.Voucher
winnerQuery := r.db.WithContext(ctx).Where("winner_number = ?", *winnerNumber)
err = winnerQuery.Find(&winnerNumberVouchers).Error
2025-09-13 15:37:26 +07:00
if err != nil {
return nil, err
}
2025-09-13 17:34:34 +07:00
// Merge the two lists, avoiding duplicates
existingIDs := make(map[int64]bool)
for _, voucher := range allVouchers {
existingIDs[voucher.ID] = true
2025-09-13 15:37:26 +07:00
}
2025-09-13 17:34:34 +07:00
for _, voucher := range winnerNumberVouchers {
if !existingIDs[voucher.ID] {
allVouchers = append(allVouchers, voucher)
2025-09-13 15:37:26 +07:00
}
}
2025-09-13 17:34:34 +07:00
}
2025-09-13 15:37:26 +07:00
2025-09-13 17:34:34 +07:00
// If still no vouchers found, get any vouchers available as fallback
if len(allVouchers) == 0 {
err = r.db.WithContext(ctx).Find(&allVouchers).Error
2025-09-13 15:37:26 +07:00
if err != nil {
return nil, err
}
}
if len(allVouchers) == 0 {
// Return empty rows if no vouchers available
return make([][]entities.Voucher, rows), nil
}
// Shuffle the vouchers for randomness
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(allVouchers), func(i, j int) {
allVouchers[i], allVouchers[j] = allVouchers[j], allVouchers[i]
})
// Calculate vouchers per row (distribute evenly)
vouchersPerRow := len(allVouchers) / rows
if vouchersPerRow == 0 {
vouchersPerRow = 1 // At least 1 voucher per row
}
// Organize vouchers into rows
voucherRows := make([][]entities.Voucher, rows)
voucherIndex := 0
for i := 0; i < rows; i++ {
// Calculate how many vouchers this row should have
remainingRows := rows - i
remainingVouchers := len(allVouchers) - voucherIndex
// Distribute remaining vouchers evenly among remaining rows
rowSize := remainingVouchers / remainingRows
if remainingVouchers%remainingRows > 0 {
rowSize++
}
// Ensure we don't exceed available vouchers
if voucherIndex+rowSize > len(allVouchers) {
rowSize = len(allVouchers) - voucherIndex
}
// Create the row
if voucherIndex < len(allVouchers) {
endIdx := voucherIndex + rowSize
if endIdx > len(allVouchers) {
endIdx = len(allVouchers)
}
voucherRows[i] = allVouchers[voucherIndex:endIdx]
voucherIndex = endIdx
} else {
voucherRows[i] = []entities.Voucher{} // Empty row
}
}
return voucherRows, nil
}
func (r *VoucherRepository) Update(ctx context.Context, voucher *entities.Voucher) error {
return r.db.WithContext(ctx).Save(voucher).Error
}
func (r *VoucherRepository) Delete(ctx context.Context, id int64) error {
return r.db.WithContext(ctx).Delete(&entities.Voucher{}, id).Error
}
func (r *VoucherRepository) MarkAsWinner(ctx context.Context, id int64) error {
return r.db.WithContext(ctx).Model(&entities.Voucher{}).Where("id = ?", id).Update("is_winner", true).Error
}