// service/event_registration_service.go package service import ( "api_bb/internal/models" "api_bb/internal/repository" "api_bb/pkg/logger" "fmt" "go.uber.org/zap" ) type EventRegistrationService interface { RegisterForEvent(registration *models.EventRegistration) error GetRegistrationByID(id uint) (*models.EventRegistration, error) GetRegistrationsByEventID(eventID uint) ([]models.EventRegistration, error) GetRegistrationsByUserID(userID uint) ([]models.EventRegistration, error) GetRegistrationByEventAndUser(eventID, userID uint) (*models.EventRegistration, error) UpdateRegistration(registration *models.EventRegistration) error CancelRegistration(id uint) error UpdateRegistrationStatus(registrationID uint, status string) error UpdateResultTime(registrationID uint, resultTime string) error CheckEventAvailability(eventID uint) (bool, error) } type eventRegistrationService struct { registrationRepo repository.EventRegistrationRepository eventRepo repository.EventRepository logger logger.LoggerInterface } func NewEventRegistrationService( registrationRepo repository.EventRegistrationRepository, eventRepo repository.EventRepository, log logger.LoggerInterface, ) EventRegistrationService { serviceLogger := log.With(zap.String("service", "event_registration")) return &eventRegistrationService{ registrationRepo: registrationRepo, eventRepo: eventRepo, logger: serviceLogger, } } // RegisterForEvent регистрирует пользователя на событие func (s *eventRegistrationService) RegisterForEvent(registration *models.EventRegistration) error { s.logger.Info("Registering user for event", zap.Uint("user_id", registration.UserID), zap.Uint("event_id", registration.EventID), ) // Проверяем существование события event, err := s.eventRepo.FindByID(registration.EventID) if err != nil { s.logger.Warn("Event not found for registration", zap.Uint("event_id", registration.EventID), zap.Error(err), ) return fmt.Errorf("event not found") } // Проверяем, открыта ли регистрация if !event.RegistrationOpen { s.logger.Warn("Registration is closed for event", zap.Uint("event_id", registration.EventID), zap.String("event_title", event.Title), ) return fmt.Errorf("registration is closed for this event") } // Проверяем, не зарегистрирован ли пользователь уже existingRegistration, err := s.registrationRepo.FindByEventAndUser(registration.EventID, registration.UserID) if err == nil && existingRegistration != nil { s.logger.Warn("User already registered for event", zap.Uint("user_id", registration.UserID), zap.Uint("event_id", registration.EventID), ) return fmt.Errorf("user already registered for this event") } // Проверяем доступность мест available, err := s.CheckEventAvailability(registration.EventID) if err != nil { s.logger.Error("Failed to check event availability", zap.Uint("event_id", registration.EventID), zap.Error(err), ) return fmt.Errorf("failed to check event availability: %w", err) } if !available { s.logger.Warn("Event is full", zap.Uint("event_id", registration.EventID), zap.String("event_title", event.Title), ) return fmt.Errorf("event is full") } // Создаем регистрацию if err := s.registrationRepo.Create(registration); err != nil { s.logger.Error("Failed to create registration", zap.Uint("user_id", registration.UserID), zap.Uint("event_id", registration.EventID), zap.Error(err), ) return fmt.Errorf("failed to register for event: %w", err) } // Обновляем счетчик участников if err := s.eventRepo.UpdateParticipantsCount(registration.EventID, event.ParticipantsCount+1); err != nil { s.logger.Error("Failed to update participants count", zap.Uint("event_id", registration.EventID), zap.Error(err), ) // Не прерываем выполнение, только логируем ошибку } s.logger.Info("User registered for event successfully", zap.Uint("user_id", registration.UserID), zap.Uint("event_id", registration.EventID), zap.String("status", registration.Status), ) return nil } // GetRegistrationByID возвращает регистрацию по ID func (s *eventRegistrationService) GetRegistrationByID(id uint) (*models.EventRegistration, error) { s.logger.Debug("Getting registration by ID", zap.Uint("registration_id", id)) registration, err := s.registrationRepo.FindByID(id) if err != nil { s.logger.Warn("Registration not found", zap.Uint("registration_id", id), zap.Error(err), ) return nil, fmt.Errorf("registration not found: %w", err) } s.logger.Debug("Registration retrieved successfully", zap.Uint("registration_id", id), zap.Uint("user_id", registration.UserID), zap.Uint("event_id", registration.EventID), ) return registration, nil } // GetRegistrationsByEventID возвращает все регистрации на событие func (s *eventRegistrationService) GetRegistrationsByEventID(eventID uint) ([]models.EventRegistration, error) { s.logger.Debug("Getting registrations by event ID", zap.Uint("event_id", eventID)) registrations, err := s.registrationRepo.FindByEventID(eventID) if err != nil { s.logger.Error("Failed to get registrations by event ID", zap.Uint("event_id", eventID), zap.Error(err), ) return nil, fmt.Errorf("failed to get registrations: %w", err) } s.logger.Debug("Registrations by event retrieved successfully", zap.Uint("event_id", eventID), zap.Int("count", len(registrations)), ) return registrations, nil } // GetRegistrationsByUserID возвращает все регистрации пользователя func (s *eventRegistrationService) GetRegistrationsByUserID(userID uint) ([]models.EventRegistration, error) { s.logger.Debug("Getting registrations by user ID", zap.Uint("user_id", userID)) registrations, err := s.registrationRepo.FindByUserID(userID) if err != nil { s.logger.Error("Failed to get registrations by user ID", zap.Uint("user_id", userID), zap.Error(err), ) return nil, fmt.Errorf("failed to get user registrations: %w", err) } s.logger.Debug("User registrations retrieved successfully", zap.Uint("user_id", userID), zap.Int("count", len(registrations)), ) return registrations, nil } // GetRegistrationByEventAndUser возвращает регистрацию по событию и пользователю func (s *eventRegistrationService) GetRegistrationByEventAndUser(eventID, userID uint) (*models.EventRegistration, error) { s.logger.Debug("Getting registration by event and user", zap.Uint("event_id", eventID), zap.Uint("user_id", userID), ) registration, err := s.registrationRepo.FindByEventAndUser(eventID, userID) if err != nil { s.logger.Debug("Registration not found for event and user", zap.Uint("event_id", eventID), zap.Uint("user_id", userID), zap.Error(err), ) return nil, fmt.Errorf("registration not found: %w", err) } s.logger.Debug("Registration by event and user retrieved successfully", zap.Uint("event_id", eventID), zap.Uint("user_id", userID), ) return registration, nil } // UpdateRegistration обновляет регистрацию func (s *eventRegistrationService) UpdateRegistration(registration *models.EventRegistration) error { s.logger.Info("Updating registration", zap.Uint("registration_id", registration.ID), zap.Uint("user_id", registration.UserID), zap.Uint("event_id", registration.EventID), ) // Проверяем существование регистрации existingRegistration, err := s.registrationRepo.FindByID(registration.ID) if err != nil { s.logger.Warn("Registration not found for update", zap.Uint("registration_id", registration.ID), zap.Error(err), ) return fmt.Errorf("registration not found") } // Сохраняем неизменяемые поля registration.CreatedAt = existingRegistration.CreatedAt if err := s.registrationRepo.Update(registration); err != nil { s.logger.Error("Failed to update registration", zap.Uint("registration_id", registration.ID), zap.Error(err), ) return fmt.Errorf("failed to update registration: %w", err) } s.logger.Info("Registration updated successfully", zap.Uint("registration_id", registration.ID), ) return nil } // CancelRegistration отменяет регистрацию func (s *eventRegistrationService) CancelRegistration(id uint) error { s.logger.Info("Canceling registration", zap.Uint("registration_id", id)) // Получаем регистрацию для получения event_id registration, err := s.registrationRepo.FindByID(id) if err != nil { s.logger.Warn("Registration not found for cancellation", zap.Uint("registration_id", id), zap.Error(err), ) return fmt.Errorf("registration not found") } if err := s.registrationRepo.Delete(id); err != nil { s.logger.Error("Failed to cancel registration", zap.Uint("registration_id", id), zap.Error(err), ) return fmt.Errorf("failed to cancel registration: %w", err) } // Обновляем счетчик участников if err := s.eventRepo.UpdateParticipantsCount(registration.EventID, registration.Event.ParticipantsCount-1); err != nil { s.logger.Error("Failed to update participants count after cancellation", zap.Uint("event_id", registration.EventID), zap.Error(err), ) // Не прерываем выполнение, только логируем ошибку } s.logger.Info("Registration canceled successfully", zap.Uint("registration_id", id), zap.Uint("event_id", registration.EventID), ) return nil } // UpdateRegistrationStatus обновляет статус регистрации func (s *eventRegistrationService) UpdateRegistrationStatus(registrationID uint, status string) error { s.logger.Info("Updating registration status", zap.Uint("registration_id", registrationID), zap.String("status", status), ) validStatuses := []string{"pending", "confirmed", "cancelled", "completed"} if !contains(validStatuses, status) { s.logger.Warn("Invalid registration status", zap.String("status", status), zap.Strings("valid_statuses", validStatuses), ) return fmt.Errorf("invalid status: %s", status) } if err := s.registrationRepo.UpdateStatus(registrationID, status); err != nil { s.logger.Error("Failed to update registration status", zap.Uint("registration_id", registrationID), zap.String("status", status), zap.Error(err), ) return fmt.Errorf("failed to update registration status: %w", err) } s.logger.Info("Registration status updated successfully", zap.Uint("registration_id", registrationID), zap.String("status", status), ) return nil } // UpdateResultTime обновляет результат забега func (s *eventRegistrationService) UpdateResultTime(registrationID uint, resultTime string) error { s.logger.Info("Updating result time", zap.Uint("registration_id", registrationID), zap.String("result_time", resultTime), ) if err := s.registrationRepo.UpdateResultTime(registrationID, resultTime); err != nil { s.logger.Error("Failed to update result time", zap.Uint("registration_id", registrationID), zap.String("result_time", resultTime), zap.Error(err), ) return fmt.Errorf("failed to update result time: %w", err) } s.logger.Info("Result time updated successfully", zap.Uint("registration_id", registrationID), zap.String("result_time", resultTime), ) return nil } // CheckEventAvailability проверяет доступность мест на событии func (s *eventRegistrationService) CheckEventAvailability(eventID uint) (bool, error) { s.logger.Debug("Checking event availability", zap.Uint("event_id", eventID)) event, err := s.eventRepo.FindByID(eventID) if err != nil { return false, fmt.Errorf("event not found: %w", err) } // Если максимальное количество участников не установлено, считаем доступным if event.MaxParticipants == 0 { return true, nil } // Получаем текущее количество подтвержденных регистраций currentCount, err := s.registrationRepo.CountByEventID(eventID) if err != nil { return false, fmt.Errorf("failed to count registrations: %w", err) } available := int(currentCount) < event.MaxParticipants s.logger.Debug("Event availability check completed", zap.Uint("event_id", eventID), zap.Int64("current_count", currentCount), zap.Int("max_participants", event.MaxParticipants), zap.Bool("available", available), ) return available, nil } // contains проверяет наличие строки в слайсе func contains(slice []string, item string) bool { for _, s := range slice { if s == item { return true } } return false }