// service/training_plan_service.go package service import ( "api_bb/internal/models" "api_bb/internal/repository" "api_bb/pkg/logger" "go.uber.org/zap" ) type TrainingPlanService interface { CreateTrainingPlan(userID uint, req *models.TrainingPlanCreateRequest) (*models.TrainingPlan, error) GetTrainingPlansByUserID(userID uint) ([]models.TrainingPlan, error) GetTrainingPlanByID(userID uint, planID uint) (*models.TrainingPlan, error) UpdateTrainingPlan(userID uint, planID uint, req *models.TrainingPlanUpdateRequest) (*models.TrainingPlan, error) DeleteTrainingPlan(userID uint, planID uint) error GetActiveTrainingPlan(userID uint) (*models.TrainingPlan, error) MarkTrainingPlanAsCompleted(userID uint, planID uint) error UpdateCurrentWeek(userID uint, planID uint, currentWeek int) error } type trainingPlanService struct { trainingPlanRepo repository.TrainingPlanRepository logger logger.LoggerInterface } func NewTrainingPlanService(trainingPlanRepo repository.TrainingPlanRepository) TrainingPlanService { return &trainingPlanService{ trainingPlanRepo: trainingPlanRepo, logger: logger.NewWrapper(logger.Get().With(zap.String("service", "training_plan"))), } } // CreateTrainingPlan создает новый план тренировок func (s *trainingPlanService) CreateTrainingPlan(userID uint, req *models.TrainingPlanCreateRequest) (*models.TrainingPlan, error) { s.logger.Debug("creating training plan", zap.Uint("user_id", userID), zap.String("title", req.Title), ) plan := &models.TrainingPlan{ UserID: userID, Title: req.Title, Description: req.Description, Weeks: req.Weeks, WorkoutsPerWeek: req.WorkoutsPerWeek, TargetDistance: req.TargetDistance, TargetDate: req.TargetDate, CurrentWeek: 1, Completed: false, } if err := s.trainingPlanRepo.Create(plan); err != nil { s.logger.Error("failed to create training plan in repository", zap.Uint("user_id", userID), zap.Error(err), ) return nil, err } s.logger.Debug("training plan created successfully", zap.Uint("user_id", userID), zap.Uint("plan_id", plan.ID), ) return plan, nil } // GetTrainingPlansByUserID возвращает все планы тренировок пользователя func (s *trainingPlanService) GetTrainingPlansByUserID(userID uint) ([]models.TrainingPlan, error) { s.logger.Debug("getting training plans for user", zap.Uint("user_id", userID)) plans, err := s.trainingPlanRepo.GetByUserID(userID) if err != nil { s.logger.Error("failed to get training plans from repository", zap.Uint("user_id", userID), zap.Error(err), ) return nil, err } s.logger.Debug("training plans retrieved successfully", zap.Uint("user_id", userID), zap.Int("count", len(plans)), ) return plans, nil } // GetTrainingPlanByID возвращает план тренировок по ID func (s *trainingPlanService) GetTrainingPlanByID(userID uint, planID uint) (*models.TrainingPlan, error) { s.logger.Debug("getting training plan by ID", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), ) plan, err := s.trainingPlanRepo.GetByID(planID) if err != nil { s.logger.Error("failed to get training plan from repository", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), zap.Error(err), ) return nil, err } // Проверяем, что план принадлежит пользователю if plan.UserID != userID { s.logger.Warn("training plan access denied - user mismatch", zap.Uint("user_id", userID), zap.Uint("plan_user_id", plan.UserID), zap.Uint("plan_id", planID), ) return nil, repository.ErrNotFound } s.logger.Debug("training plan retrieved successfully", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), ) return plan, nil } // UpdateTrainingPlan обновляет план тренировок func (s *trainingPlanService) UpdateTrainingPlan(userID uint, planID uint, req *models.TrainingPlanUpdateRequest) (*models.TrainingPlan, error) { s.logger.Debug("updating training plan", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), ) // Сначала получаем существующий план plan, err := s.GetTrainingPlanByID(userID, planID) if err != nil { return nil, err } // Обновляем только переданные поля if req.Title != "" { plan.Title = req.Title } if req.Description != "" { plan.Description = req.Description } if req.Weeks > 0 { plan.Weeks = req.Weeks } if req.WorkoutsPerWeek > 0 { plan.WorkoutsPerWeek = req.WorkoutsPerWeek } if req.TargetDistance != "" { plan.TargetDistance = req.TargetDistance } if !req.TargetDate.IsZero() { plan.TargetDate = req.TargetDate } // Сохраняем обновления if err := s.trainingPlanRepo.Update(plan); err != nil { s.logger.Error("failed to update training plan in repository", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), zap.Error(err), ) return nil, err } s.logger.Debug("training plan updated successfully", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), ) return plan, nil } // DeleteTrainingPlan удаляет план тренировок func (s *trainingPlanService) DeleteTrainingPlan(userID uint, planID uint) error { s.logger.Debug("deleting training plan", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), ) // Проверяем, что план существует и принадлежит пользователю _, err := s.GetTrainingPlanByID(userID, planID) if err != nil { return err } // Удаляем план if err := s.trainingPlanRepo.Delete(planID); err != nil { s.logger.Error("failed to delete training plan from repository", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), zap.Error(err), ) return err } s.logger.Debug("training plan deleted successfully", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), ) return nil } // GetActiveTrainingPlan возвращает активный план тренировок пользователя func (s *trainingPlanService) GetActiveTrainingPlan(userID uint) (*models.TrainingPlan, error) { s.logger.Debug("getting active training plan for user", zap.Uint("user_id", userID)) plan, err := s.trainingPlanRepo.GetActivePlan(userID) if err != nil { s.logger.Error("failed to get active training plan from repository", zap.Uint("user_id", userID), zap.Error(err), ) return nil, err } s.logger.Debug("active training plan retrieved successfully", zap.Uint("user_id", userID), zap.Uint("plan_id", plan.ID), ) return plan, nil } // MarkTrainingPlanAsCompleted помечает план тренировок как завершенный func (s *trainingPlanService) MarkTrainingPlanAsCompleted(userID uint, planID uint) error { s.logger.Debug("marking training plan as completed", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), ) // Проверяем, что план существует и принадлежит пользователю _, err := s.GetTrainingPlanByID(userID, planID) if err != nil { return err } // Помечаем как завершенный if err := s.trainingPlanRepo.MarkAsCompleted(planID); err != nil { s.logger.Error("failed to mark training plan as completed in repository", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), zap.Error(err), ) return err } s.logger.Debug("training plan marked as completed successfully", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), ) return nil } // UpdateCurrentWeek обновляет текущую неделю плана тренировок func (s *trainingPlanService) UpdateCurrentWeek(userID uint, planID uint, currentWeek int) error { s.logger.Debug("updating current week for training plan", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), zap.Int("current_week", currentWeek), ) // Проверяем, что план существует и принадлежит пользователю _, err := s.GetTrainingPlanByID(userID, planID) if err != nil { return err } // Обновляем текущую неделю if err := s.trainingPlanRepo.UpdateCurrentWeek(planID, currentWeek); err != nil { s.logger.Error("failed to update current week in repository", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), zap.Error(err), ) return err } s.logger.Debug("current week updated successfully", zap.Uint("user_id", userID), zap.Uint("plan_id", planID), zap.Int("current_week", currentWeek), ) return nil }