package handlers import ( "math/rand" "net/http" "strconv" "time" "sale/internal/models" "sale/internal/schemas" "sale/internal/utils" "github.com/gin-gonic/gin" ) type LotteryHandler struct{} func NewLotteryHandler() *LotteryHandler { return &LotteryHandler{} } func (h *LotteryHandler) List(c *gin.Context) { var lotteries []models.Lottery utils.DB.Where("is_active = ?", true).Preload("Prizes").Order("created_at DESC").Find(&lotteries) c.JSON(http.StatusOK, gin.H{"data": lotteries}) } func (h *LotteryHandler) GetByID(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var lottery models.Lottery if err := utils.DB.Preload("Prizes").First(&lottery, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Lottery not found"}) return } c.JSON(http.StatusOK, gin.H{"data": lottery}) } func (h *LotteryHandler) Register(c *gin.Context) { userID := c.GetUint("user_id") id, _ := strconv.Atoi(c.Param("id")) var lottery models.Lottery if err := utils.DB.First(&lottery, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Lottery not found"}) return } now := time.Now() if now.Before(lottery.StartTime) || now.After(lottery.EndTime) { c.JSON(http.StatusBadRequest, gin.H{"error": "Lottery is not in registration period"}) return } var existing models.LotteryParticipant if err := utils.DB.Where("lottery_id = ? AND user_id = ?", id, userID).First(&existing).Error; err == nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Already registered"}) return } var totalSpent float64 utils.DB.Model(&models.Order{}). Where("user_id = ? AND status = ?", userID, models.OrderStatusCompleted). Select("COALESCE(SUM(total_amount), 0)"). Scan(&totalSpent) participant := models.LotteryParticipant{ LotteryID: lottery.ID, UserID: userID, PurchaseWeight: int(totalSpent), } utils.DB.Create(&participant) c.JSON(http.StatusOK, gin.H{"message": "Registered successfully"}) } func (h *LotteryHandler) Create(c *gin.Context) { var req schemas.CreateLotteryRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } startTime, _ := time.Parse(time.RFC3339, req.StartTime) endTime, _ := time.Parse(time.RFC3339, req.EndTime) lottery := models.Lottery{ Name: req.Name, Description: req.Description, StartTime: startTime, EndTime: endTime, Cycle: req.Cycle, DailyQuota: req.DailyQuota, TotalQuota: req.TotalQuota, RegistrationValidity: req.RegistrationValidity, IsActive: true, } utils.DB.Create(&lottery) c.JSON(http.StatusCreated, gin.H{"data": lottery}) } func (h *LotteryHandler) Update(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var lottery models.Lottery if err := utils.DB.First(&lottery, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Lottery not found"}) return } var req schemas.UpdateLotteryRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } updates := make(map[string]interface{}) if req.Name != nil { updates["name"] = *req.Name } if req.Description != nil { updates["description"] = *req.Description } if req.StartTime != nil { t, _ := time.Parse(time.RFC3339, *req.StartTime) updates["start_time"] = t } if req.EndTime != nil { t, _ := time.Parse(time.RFC3339, *req.EndTime) updates["end_time"] = t } if req.Cycle != nil { updates["cycle"] = *req.Cycle } if req.DailyQuota != nil { updates["daily_quota"] = *req.DailyQuota } if req.TotalQuota != nil { updates["total_quota"] = *req.TotalQuota } if req.RegistrationValidity != nil { updates["registration_validity"] = *req.RegistrationValidity } if req.IsActive != nil { updates["is_active"] = *req.IsActive } utils.DB.Model(&lottery).Updates(updates) c.JSON(http.StatusOK, gin.H{"data": lottery}) } func (h *LotteryHandler) Delete(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) utils.DB.Delete(&models.Lottery{}, id) c.JSON(http.StatusOK, gin.H{"message": "Lottery deleted successfully"}) } func (h *LotteryHandler) AddPrize(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var req schemas.AddLotteryPrizeRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } prize := models.LotteryPrize{ LotteryID: uint(id), Name: req.Name, Type: req.Type, Quantity: req.Quantity, Weight: req.Weight, CreditReward: req.CreditReward, DrawMode: req.DrawMode, } utils.DB.Create(&prize) c.JSON(http.StatusCreated, gin.H{"data": prize}) } func (h *LotteryHandler) Draw(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var lottery models.Lottery if err := utils.DB.Preload("Prizes").First(&lottery, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Lottery not found"}) return } var participants []models.LotteryParticipant utils.DB.Where("lottery_id = ?", id).Find(&participants) if len(participants) == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "No participants"}) return } var winners []models.LotteryWinner rng := rand.New(rand.NewSource(time.Now().UnixNano())) for _, prize := range lottery.Prizes { remaining := prize.Quantity var weightParticipants []models.LotteryParticipant var randomParticipants []models.LotteryParticipant if prize.DrawMode == models.DrawModeWeight { weightParticipants = participants } else { randomParticipants = participants } selectedParticipants := weightParticipants if len(selectedParticipants) == 0 { selectedParticipants = randomParticipants } totalWeight := 0 for _, p := range selectedParticipants { w := p.PurchaseWeight if w < 1 { w = 1 } totalWeight += w } selected := make(map[uint]bool) for remaining > 0 && len(selected) < len(selectedParticipants) { r := rng.Intn(totalWeight) cumWeight := 0 for _, p := range selectedParticipants { if selected[p.UserID] { continue } w := p.PurchaseWeight if w < 1 { w = 1 } cumWeight += w if cumWeight > r { winner := models.LotteryWinner{ LotteryID: lottery.ID, PrizeID: prize.ID, UserID: p.UserID, DrawnAt: time.Now(), } winners = append(winners, winner) selected[p.UserID] = true remaining-- if prize.Type == models.PrizeTypeCredit && prize.CreditReward != nil { utils.DB.Model(&models.User{}).Where("id = ?", p.UserID). UpdateColumn("purchase_credits", utils.DB.Raw("purchase_credits + ?", *prize.CreditReward)) } break } } } } if len(winners) > 0 { utils.DB.Create(&winners) } c.JSON(http.StatusOK, gin.H{"data": winners, "message": "Draw completed successfully"}) }