// handlers/event_registration_handler.go package handlers import ( "api_bb/internal/models" "api_bb/internal/service" "api_bb/pkg/logger" "api_bb/pkg/middleware" "api_bb/pkg/utils" "encoding/json" "net/http" "strconv" "go.uber.org/zap" ) type EventRegistrationHandler struct { logger logger.LoggerInterface registrationService service.EventRegistrationService } func NewEventRegistrationHandler(registrationService service.EventRegistrationService) *EventRegistrationHandler { return &EventRegistrationHandler{ logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "event_registration"))), registrationService: registrationService, } } // RegisterForEventRequest - DTO для регистрации на событие type RegisterForEventRequest struct { EventID uint `json:"event_id" validate:"required"` Notes string `json:"notes" validate:"max=500"` } // UpdateRegistrationRequest - DTO для обновления регистрации type UpdateRegistrationRequest struct { Notes string `json:"notes" validate:"max=500"` } // RegistrationResponse - DTO для ответа с регистрацией type RegistrationResponse struct { ID uint `json:"id"` UserID uint `json:"user_id"` EventID uint `json:"event_id"` Status string `json:"status"` Notes string `json:"notes"` ResultTime string `json:"result_time"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` Event EventResponse `json:"event,omitempty"` } // RegisterForEvent регистрирует пользователя на событие func (h *EventRegistrationHandler) RegisterForEvent(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling register for event request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Проверяем аутентификацию user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("register for event failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } var req RegisterForEventRequest if err := utils.DecodeJSONBody(w, r, &req); err != nil { h.logger.Error("failed to decode request body", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error()) return } // Валидация if err := utils.ValidateStruct(req); err != nil { h.logger.Warn("validation failed for register for event", zap.Error(err)) utils.RespondWithValidationError(w, err) return } // Создаем модель регистрации registration := &models.EventRegistration{ UserID: user.ID, EventID: req.EventID, Status: "pending", Notes: req.Notes, } if err := h.registrationService.RegisterForEvent(registration); err != nil { h.logger.Error("failed to register for event", zap.Uint("user_id", user.ID), zap.Uint("event_id", req.EventID), zap.Error(err), ) statusCode := http.StatusInternalServerError if err.Error() == "event not found" { statusCode = http.StatusNotFound } else if err.Error() == "user already registered for this event" { statusCode = http.StatusConflict } else if err.Error() == "registration is closed for this event" { statusCode = http.StatusForbidden } else if err.Error() == "event is full" { statusCode = http.StatusConflict } utils.RespondWithError(w, statusCode, "Failed to register for event: "+err.Error()) return } h.logger.Info("user registered for event successfully", zap.Uint("user_id", user.ID), zap.Uint("event_id", req.EventID), zap.Uint("registration_id", registration.ID), ) utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{ "message": "Successfully registered for event", "registration": toRegistrationResponse(registration), }) } // GetRegistration возвращает регистрацию по ID func (h *EventRegistrationHandler) GetRegistration(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling get registration request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Проверяем аутентификацию user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("get registration failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } // Извлекаем ID регистрации registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32) if err != nil { h.logger.Warn("invalid registration ID", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID") return } registration, err := h.registrationService.GetRegistrationByID(uint(registrationID)) if err != nil { h.logger.Warn("registration not found", zap.Uint("registration_id", uint(registrationID)), zap.Error(err), ) utils.RespondWithError(w, http.StatusNotFound, "Registration not found") return } // Проверяем права доступа (пользователь может видеть только свои регистрации, админ - все) if user.Role != "admin" && registration.UserID != user.ID { h.logger.Warn("access denied to registration", zap.Uint("user_id", user.ID), zap.Uint("registration_user_id", registration.UserID), ) utils.RespondWithError(w, http.StatusForbidden, "Access denied") return } h.logger.Info("registration retrieved successfully", zap.Uint("registration_id", uint(registrationID)), zap.Uint("user_id", user.ID), ) utils.RespondWithJSON(w, http.StatusOK, toRegistrationResponse(registration)) } // GetUserRegistrations возвращает все регистрации пользователя func (h *EventRegistrationHandler) GetUserRegistrations(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling get user registrations request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Проверяем аутентификацию user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("get user registrations failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } registrations, err := h.registrationService.GetRegistrationsByUserID(user.ID) if err != nil { h.logger.Error("failed to get user registrations", zap.Uint("user_id", user.ID), zap.Error(err), ) utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get registrations: "+err.Error()) return } var registrationResponses []RegistrationResponse for _, registration := range registrations { registrationResponses = append(registrationResponses, toRegistrationResponse(®istration)) } h.logger.Info("user registrations retrieved successfully", zap.Uint("user_id", user.ID), zap.Int("registrations_count", len(registrationResponses)), ) utils.RespondWithJSON(w, http.StatusOK, registrationResponses) } // GetEventRegistrations возвращает все регистрации на событие func (h *EventRegistrationHandler) GetEventRegistrations(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling get event registrations request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Проверяем аутентификацию и права (только админы могут видеть все регистрации на событие) user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("get event registrations failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } if user.Role != "admin" { h.logger.Warn("get event registrations failed - insufficient permissions", zap.Uint("user_id", user.ID), zap.String("user_role", user.Role), ) utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions") return } // Извлекаем ID события eventID, err := strconv.ParseUint(r.PathValue("eventId"), 10, 32) if err != nil { h.logger.Warn("invalid event ID", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID") return } registrations, err := h.registrationService.GetRegistrationsByEventID(uint(eventID)) if err != nil { h.logger.Error("failed to get event registrations", zap.Uint("event_id", uint(eventID)), zap.Error(err), ) utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get registrations: "+err.Error()) return } var registrationResponses []RegistrationResponse for _, registration := range registrations { registrationResponses = append(registrationResponses, toRegistrationResponse(®istration)) } h.logger.Info("event registrations retrieved successfully", zap.Uint("event_id", uint(eventID)), zap.Int("registrations_count", len(registrationResponses)), ) utils.RespondWithJSON(w, http.StatusOK, registrationResponses) } // CancelRegistration отменяет регистрацию func (h *EventRegistrationHandler) CancelRegistration(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling cancel registration request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Проверяем аутентификацию user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("cancel registration failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } // Извлекаем ID регистрации registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32) if err != nil { h.logger.Warn("invalid registration ID", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID") return } // Проверяем права доступа registration, err := h.registrationService.GetRegistrationByID(uint(registrationID)) if err != nil { h.logger.Warn("registration not found for cancellation", zap.Uint("registration_id", uint(registrationID)), zap.Error(err), ) utils.RespondWithError(w, http.StatusNotFound, "Registration not found") return } if user.Role != "admin" && registration.UserID != user.ID { h.logger.Warn("access denied to cancel registration", zap.Uint("user_id", user.ID), zap.Uint("registration_user_id", registration.UserID), ) utils.RespondWithError(w, http.StatusForbidden, "Access denied") return } if err := h.registrationService.CancelRegistration(uint(registrationID)); err != nil { h.logger.Error("failed to cancel registration", zap.Uint("registration_id", uint(registrationID)), zap.Error(err), ) utils.RespondWithError(w, http.StatusInternalServerError, "Failed to cancel registration: "+err.Error()) return } h.logger.Info("registration cancelled successfully", zap.Uint("registration_id", uint(registrationID)), zap.Uint("user_id", user.ID), ) utils.RespondWithJSON(w, http.StatusOK, map[string]string{ "message": "Registration cancelled successfully", }) } // UpdateRegistrationStatus обновляет статус регистрации func (h *EventRegistrationHandler) UpdateRegistrationStatus(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling update registration status request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Проверяем аутентификацию и права (только админы) user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("update registration status failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } if user.Role != "admin" { h.logger.Warn("update registration status failed - insufficient permissions", zap.Uint("user_id", user.ID), zap.String("user_role", user.Role), ) utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions") return } // Извлекаем ID регистрации registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32) if err != nil { h.logger.Warn("invalid registration ID", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID") return } var req struct { Status string `json:"status" validate:"required,oneof=pending confirmed cancelled completed"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { h.logger.Error("failed to decode request body", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload") return } if err := utils.ValidateStruct(req); err != nil { h.logger.Warn("validation failed for update registration status", zap.Error(err)) utils.RespondWithValidationError(w, err) return } if err := h.registrationService.UpdateRegistrationStatus(uint(registrationID), req.Status); err != nil { h.logger.Error("failed to update registration status", zap.Uint("registration_id", uint(registrationID)), zap.String("status", req.Status), zap.Error(err), ) utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update registration status: "+err.Error()) return } h.logger.Info("registration status updated successfully", zap.Uint("registration_id", uint(registrationID)), zap.String("status", req.Status), ) utils.RespondWithJSON(w, http.StatusOK, map[string]string{ "message": "Registration status updated successfully", }) } // UpdateResultTime обновляет результат забега func (h *EventRegistrationHandler) UpdateResultTime(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling update result time request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Проверяем аутентификацию и права (только админы) user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("update result time failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } if user.Role != "admin" { h.logger.Warn("update result time failed - insufficient permissions", zap.Uint("user_id", user.ID), zap.String("user_role", user.Role), ) utils.RespondWithError(w, http.StatusForbidden, "Insufficient permissions") return } // Извлекаем ID регистрации registrationID, err := strconv.ParseUint(r.PathValue("id"), 10, 32) if err != nil { h.logger.Warn("invalid registration ID", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid registration ID") return } var req struct { ResultTime string `json:"result_time" validate:"required,max=20"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { h.logger.Error("failed to decode request body", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload") return } if err := utils.ValidateStruct(req); err != nil { h.logger.Warn("validation failed for update result time", zap.Error(err)) utils.RespondWithValidationError(w, err) return } if err := h.registrationService.UpdateResultTime(uint(registrationID), req.ResultTime); err != nil { h.logger.Error("failed to update result time", zap.Uint("registration_id", uint(registrationID)), zap.String("result_time", req.ResultTime), zap.Error(err), ) utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update result time: "+err.Error()) return } h.logger.Info("result time updated successfully", zap.Uint("registration_id", uint(registrationID)), zap.String("result_time", req.ResultTime), ) utils.RespondWithJSON(w, http.StatusOK, map[string]string{ "message": "Result time updated successfully", }) } // CheckEventAvailability проверяет доступность мест на событии func (h *EventRegistrationHandler) CheckEventAvailability(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling check event availability request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Извлекаем ID события eventID, err := strconv.ParseUint(r.PathValue("eventId"), 10, 32) if err != nil { h.logger.Warn("invalid event ID", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid event ID") return } available, err := h.registrationService.CheckEventAvailability(uint(eventID)) if err != nil { h.logger.Error("failed to check event availability", zap.Uint("event_id", uint(eventID)), zap.Error(err), ) utils.RespondWithError(w, http.StatusInternalServerError, "Failed to check availability: "+err.Error()) return } h.logger.Info("event availability checked successfully", zap.Uint("event_id", uint(eventID)), zap.Bool("available", available), ) utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{ "event_id": eventID, "available": available, }) } // toRegistrationResponse преобразует модель регистрации в response DTO func toRegistrationResponse(registration *models.EventRegistration) RegistrationResponse { response := RegistrationResponse{ ID: registration.ID, UserID: registration.UserID, EventID: registration.EventID, Status: registration.Status, Notes: registration.Notes, ResultTime: registration.ResultTime, CreatedAt: registration.CreatedAt.Format("2006-01-02 15:04:05"), UpdatedAt: registration.UpdatedAt.Format("2006-01-02 15:04:05"), } // Включаем информацию о событии, если она загружена if registration.Event != nil { response.Event = toEventResponse(registration.Event) } return response }