package controllers import ( "fotbal-club/internal/models" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // PollControllerRefactored demonstrates refactored poll controller using utility helpers // This is an example showing how to use the new utilities type PollControllerRefactored struct { DB *gorm.DB } // NewPollControllerRefactored creates a new refactored poll controller func NewPollControllerRefactored(db *gorm.DB) *PollControllerRefactored { return &PollControllerRefactored{DB: db} } // GetPolls returns paginated list of polls with filtering and search // GET /api/v1/polls?search=election&active=true&sort=created_at:desc&page=1&page_size=20 func (pcr *PollControllerRefactored) GetPolls(c *gin.Context) { // Build query with search, filter, and sort query := QueryParser.BuildQueryChain(c, pcr.DB.Model(&models.Poll{})). WithSearch("title", "description"). WithSort("created_at", "desc"). WithBoolFilter("active", "active"). Build() // Paginate and preload relationships var polls []models.Poll meta, err := Paginator.PaginateWithPreload(c, query, &polls, "Options") if err != nil { Respond.InternalError(c, "Failed to retrieve polls") return } Respond.SuccessWithMeta(c, polls, meta, "Polls retrieved successfully") } // GetPoll returns a single poll by ID // GET /api/v1/polls/:id func (pcr *PollControllerRefactored) GetPoll(c *gin.Context) { id := c.Param("id") var poll models.Poll if err := pcr.DB.Preload("Options").First(&poll, id).Error; err != nil { if err == gorm.ErrRecordNotFound { Respond.NotFound(c, "Poll not found") return } Respond.InternalError(c, "Failed to retrieve poll") return } Respond.Success(c, poll, "Poll retrieved successfully") } // CreatePoll creates a new poll with validation // POST /api/v1/polls func (pcr *PollControllerRefactored) CreatePoll(c *gin.Context) { type CreatePollRequest struct { Title string `json:"title" validate:"required,min=3,max=200"` Description string `json:"description" validate:"omitempty,max=500"` Active *bool `json:"active"` MultiChoice *bool `json:"multi_choice"` Options []string `json:"options" validate:"required,min=2"` } var req CreatePollRequest if err := c.ShouldBindJSON(&req); err != nil { Respond.BadRequest(c, "Invalid JSON: "+err.Error()) return } // Validate request if !Validator.ValidateAndRespond(c, req) { return } // Sanitize inputs req.Title = Validator.SanitizeString(req.Title) req.Description = Validator.SanitizeString(req.Description) // Set defaults active := true if req.Active != nil { active = *req.Active } multiChoice := false if req.MultiChoice != nil { multiChoice = *req.MultiChoice } status := "draft" if active { status = "active" } maxChoices := 1 if multiChoice { maxChoices = len(req.Options) if maxChoices == 0 { maxChoices = 1 } } // Create poll poll := models.Poll{ Title: req.Title, Description: req.Description, Status: status, AllowMultiple: multiChoice, MaxChoices: maxChoices, } if err := pcr.DB.Create(&poll).Error; err != nil { Respond.InternalError(c, "Failed to create poll") return } // Create options for _, optionText := range req.Options { option := models.PollOption{ PollID: poll.ID, Text: Validator.SanitizeString(optionText), } if err := pcr.DB.Create(&option).Error; err != nil { Respond.InternalError(c, "Failed to create poll option") return } } // Reload with options pcr.DB.Preload("Options").First(&poll, poll.ID) // Log creation if AuditLogger != nil { _ = AuditLogger.LogCreate(c, "Poll", poll.ID, "Poll created: "+poll.Title) } Respond.Created(c, poll, "Poll created successfully") } // UpdatePoll updates an existing poll // PUT /api/v1/polls/:id func (pcr *PollControllerRefactored) UpdatePoll(c *gin.Context) { id := c.Param("id") var poll models.Poll if err := pcr.DB.First(&poll, id).Error; err != nil { if err == gorm.ErrRecordNotFound { Respond.NotFound(c, "Poll not found") return } Respond.InternalError(c, "Failed to retrieve poll") return } // Store old values for audit oldTitle := poll.Title oldStatus := poll.Status type UpdatePollRequest struct { Title string `json:"title" validate:"omitempty,min=3,max=200"` Description string `json:"description" validate:"omitempty,max=500"` Active *bool `json:"active"` MultiChoice *bool `json:"multi_choice"` } var req UpdatePollRequest if err := c.ShouldBindJSON(&req); err != nil { Respond.BadRequest(c, "Invalid JSON: "+err.Error()) return } if !Validator.ValidateAndRespond(c, req) { return } // Apply updates if req.Title != "" { poll.Title = Validator.SanitizeString(req.Title) } if req.Description != "" { poll.Description = Validator.SanitizeString(req.Description) } if req.Active != nil { if *req.Active { poll.Status = "active" } else { poll.Status = "draft" } } if req.MultiChoice != nil { poll.AllowMultiple = *req.MultiChoice if poll.AllowMultiple && poll.MaxChoices < 1 { poll.MaxChoices = len(poll.Options) if poll.MaxChoices == 0 { poll.MaxChoices = 1 } } if !poll.AllowMultiple { poll.MaxChoices = 1 } } if err := pcr.DB.Save(&poll).Error; err != nil { Respond.InternalError(c, "Failed to update poll") return } // Log update if AuditLogger != nil { before := map[string]interface{}{ "title": oldTitle, "status": oldStatus, } after := map[string]interface{}{ "title": poll.Title, "status": poll.Status, } _ = AuditLogger.LogUpdate(c, "Poll", poll.ID, "Poll updated", before, after) } Respond.Success(c, poll, "Poll updated successfully") } // DeletePoll deletes a poll // DELETE /api/v1/polls/:id func (pcr *PollControllerRefactored) DeletePoll(c *gin.Context) { id := c.Param("id") var poll models.Poll if err := pcr.DB.First(&poll, id).Error; err != nil { if err == gorm.ErrRecordNotFound { Respond.NotFound(c, "Poll not found") return } Respond.InternalError(c, "Failed to retrieve poll") return } title := poll.Title pollID := poll.ID // Delete associated options first if err := pcr.DB.Where("poll_id = ?", poll.ID).Delete(&models.PollOption{}).Error; err != nil { Respond.InternalError(c, "Failed to delete poll options") return } // Delete poll if err := pcr.DB.Delete(&poll).Error; err != nil { Respond.InternalError(c, "Failed to delete poll") return } // Log deletion if AuditLogger != nil { _ = AuditLogger.LogDelete(c, "Poll", pollID, "Poll deleted: "+title) } Respond.NoContent(c) } // BatchDeletePolls deletes multiple polls // POST /api/v1/polls/batch-delete // Body: {"ids": [1, 2, 3]} func (pcr *PollControllerRefactored) BatchDeletePolls(c *gin.Context) { if BatchOps == nil { BatchOps = NewBatchOperationsController(pcr.DB) } BatchOps.BatchDelete(c, &models.Poll{}, "polls") } // BatchActivatePolls activates multiple polls // POST /api/v1/polls/batch-activate // Body: {"ids": [1, 2, 3]} func (pcr *PollControllerRefactored) BatchActivatePolls(c *gin.Context) { if BatchOps == nil { BatchOps = NewBatchOperationsController(pcr.DB) } BatchOps.BatchUpdate(c, &models.Poll{}, "polls", []string{"active"}) } // VotePoll records a vote on a poll // POST /api/v1/polls/:id/vote // Body: {"option_id": 1} or {"option_ids": [1, 2]} for multi-choice func (pcr *PollControllerRefactored) VotePoll(c *gin.Context) { id := c.Param("id") var poll models.Poll if err := pcr.DB.Preload("Options").First(&poll, id).Error; err != nil { if err == gorm.ErrRecordNotFound { Respond.NotFound(c, "Poll not found") return } Respond.InternalError(c, "Failed to retrieve poll") return } if !poll.IsActive() { Respond.BadRequest(c, "This poll is not active") return } var req struct { OptionID *uint `json:"option_id"` OptionIDs []uint `json:"option_ids"` } if err := c.ShouldBindJSON(&req); err != nil { Respond.BadRequest(c, "Invalid JSON: "+err.Error()) return } // Determine which options to vote for var optionIDs []uint if req.OptionID != nil { optionIDs = []uint{*req.OptionID} } else if len(req.OptionIDs) > 0 { optionIDs = req.OptionIDs } else { Respond.BadRequest(c, "No option selected") return } // Validate multi-choice if !poll.AllowMultiple && len(optionIDs) > 1 { Respond.BadRequest(c, "This poll does not allow multiple choices") return } if poll.AllowMultiple && poll.MaxChoices > 0 && len(optionIDs) > poll.MaxChoices { Respond.BadRequest(c, "You have selected too many options") return } // Get user ID (if authenticated) var userID *uint if user, exists := c.Get("user"); exists { if u, ok := user.(*models.User); ok && u != nil { userID = &u.ID } } // Check if already voted (if user is authenticated) if userID != nil { var existingVote models.PollVote if err := pcr.DB.Where("poll_id = ? AND user_id = ?", poll.ID, userID).First(&existingVote).Error; err == nil { Respond.Conflict(c, "You have already voted on this poll") return } } // Record votes for _, optionID := range optionIDs { vote := models.PollVote{ PollID: poll.ID, OptionID: optionID, UserID: userID, } if err := pcr.DB.Create(&vote).Error; err != nil { Respond.InternalError(c, "Failed to record vote") return } } // Log vote if AuditLogger != nil { _ = AuditLogger.LogEntry(c, "VOTE", "Poll", &poll.ID, "Vote recorded on poll: "+poll.Title, nil) } Respond.Success(c, gin.H{"voted": true}, "Vote recorded successfully") } // GetPollResults returns poll results // GET /api/v1/polls/:id/results func (pcr *PollControllerRefactored) GetPollResults(c *gin.Context) { id := c.Param("id") var poll models.Poll if err := pcr.DB.Preload("Options").First(&poll, id).Error; err != nil { if err == gorm.ErrRecordNotFound { Respond.NotFound(c, "Poll not found") return } Respond.InternalError(c, "Failed to retrieve poll") return } // Count votes for each option type OptionResult struct { OptionID uint `json:"option_id"` OptionText string `json:"option_text"` VoteCount int64 `json:"vote_count"` } results := make([]OptionResult, 0, len(poll.Options)) var totalVotes int64 for _, option := range poll.Options { var count int64 pcr.DB.Model(&models.PollVote{}).Where("option_id = ?", option.ID).Count(&count) results = append(results, OptionResult{ OptionID: option.ID, OptionText: option.Text, VoteCount: count, }) totalVotes += count } response := gin.H{ "poll_id": poll.ID, "poll_title": poll.Title, "total_votes": totalVotes, "results": results, } Respond.Success(c, response, "Poll results retrieved successfully") }