// services/personal_best_service.go package service import ( "api_bb/internal/models" "api_bb/internal/repository" "fmt" "time" "gorm.io/gorm" ) type PersonalBestService struct { pbRepo repository.PersonalBestRepository userStatsService UserStatsService } func NewPersonalBestService(pbRepo repository.PersonalBestRepository, userStatsService UserStatsService) *PersonalBestService { return &PersonalBestService{ pbRepo: pbRepo, userStatsService: userStatsService, } } // CreatePersonalBest создает новый личный рекорд func (s *PersonalBestService) CreatePersonalBest(userID uint, req models.PersonalBestCreateRequest) (*models.PersonalBest, error) { // Вычисляем темп, если не предоставлен pace := req.Pace if pace == "" { calculatedPace, err := s.pbRepo.CalculatePace(req.Time, req.DistanceType) if err != nil { return nil, err } pace = calculatedPace } // Проверяем, является ли это личным рекордом isBest, err := s.pbRepo.ExistsBetterTime(userID, req.DistanceType, req.Time) if err != nil { return nil, err } personalBest := &models.PersonalBest{ UserID: userID, DistanceType: req.DistanceType, Time: req.Time, Pace: pace, Date: req.Date, EventName: req.EventName, Location: req.Location, Verified: false, // По умолчанию не подтвержден } if err := s.pbRepo.Create(personalBest); err != nil { return nil, err } if !isBest { if err := s.userStatsService.UpdatePersonalBest(userID, string(req.DistanceType), req.Time); err != nil { // Логируем ошибку, но не прерываем выполнение fmt.Printf("Failed to update user stats: %v\n", err) } } return personalBest, nil } // GetPersonalBestByID возвращает личный рекорд по ID func (s *PersonalBestService) GetPersonalBestByID(id uint) (*models.PersonalBest, error) { return s.pbRepo.GetByID(id) } // GetUserPersonalBests возвращает все личные рекорды пользователя func (s *PersonalBestService) GetUserPersonalBests(userID uint) ([]models.PersonalBest, error) { return s.pbRepo.GetByUserID(userID) } // GetPersonalBestsByDistance возвращает личные рекорды по дистанции func (s *PersonalBestService) GetPersonalBestsByDistance(userID uint, distanceType models.DistanceType) ([]models.PersonalBest, error) { return s.pbRepo.GetByUserAndDistance(userID, distanceType) } // GetBestByDistance возвращает лучший результат на дистанции func (s *PersonalBestService) GetBestByDistance(userID uint, distanceType models.DistanceType) (*models.PersonalBest, error) { return s.pbRepo.GetBestByDistance(userID, distanceType) } // UpdatePersonalBest обновляет личный рекорд func (s *PersonalBestService) UpdatePersonalBest(id uint, userID uint, req models.PersonalBestUpdateRequest) (*models.PersonalBest, error) { // Получаем существующий рекорд pb, err := s.pbRepo.GetByID(id) if err != nil { return nil, err } // Проверяем, что рекорд принадлежит пользователю if pb.UserID != userID { return nil, gorm.ErrRecordNotFound } // Обновляем поля if req.DistanceType != "" { pb.DistanceType = req.DistanceType } if req.Time != "" { pb.Time = req.Time // Пересчитываем темп при изменении времени if req.Pace == "" { calculatedPace, err := s.pbRepo.CalculatePace(req.Time, pb.DistanceType) if err != nil { return nil, err } pb.Pace = calculatedPace } } if req.Pace != "" { pb.Pace = req.Pace } if !req.Date.IsZero() { pb.Date = req.Date } if req.EventName != "" { pb.EventName = req.EventName } if req.Location != "" { pb.Location = req.Location } pb.Verified = req.Verified if err := s.pbRepo.Update(pb); err != nil { return nil, err } return pb, nil } // DeletePersonalBest удаляет личный рекорд func (s *PersonalBestService) DeletePersonalBest(id uint, userID uint) error { // Проверяем, что рекорд принадлежит пользователю pb, err := s.pbRepo.GetByID(id) if err != nil { return err } if pb.UserID != userID { return gorm.ErrRecordNotFound } return s.pbRepo.Delete(id) } // GetVerifiedPersonalBests возвращает подтвержденные личные рекорды func (s *PersonalBestService) GetVerifiedPersonalBests(userID uint) ([]models.PersonalBest, error) { return s.pbRepo.GetVerifiedByUserID(userID) } // GetPersonalBestsByDateRange возвращает личные рекорды за период func (s *PersonalBestService) GetPersonalBestsByDateRange(userID uint, startDate, endDate time.Time) ([]models.PersonalBest, error) { return s.pbRepo.GetByDateRange(userID, startDate, endDate) } // GetRecentPersonalBests возвращает последние личные рекорды func (s *PersonalBestService) GetRecentPersonalBests(userID uint, limit int) ([]models.PersonalBest, error) { return s.pbRepo.GetRecentPersonalBests(userID, limit) } // GetPersonalBestsByEvent возвращает личные рекорды по названию события func (s *PersonalBestService) GetPersonalBestsByEvent(userID uint, eventName string) ([]models.PersonalBest, error) { return s.pbRepo.GetByEventName(userID, eventName) } // GetPersonalBestsSummary возвращает сводку лучших результатов func (s *PersonalBestService) GetPersonalBestsSummary(userID uint) (*models.PersonalBestsSummary, error) { return s.pbRepo.GetPersonalBestsSummary(userID) } // VerifyPersonalBest подтверждает личный рекорд func (s *PersonalBestService) VerifyPersonalBest(id uint, userID uint) error { pb, err := s.pbRepo.GetByID(id) if err != nil { return err } // Проверяем, что рекорд принадлежит пользователю if pb.UserID != userID { return gorm.ErrRecordNotFound } pb.Verified = true return s.pbRepo.Update(pb) } // CalculatePace вычисляет темп для времени и дистанции func (s *PersonalBestService) CalculatePace(timeStr string, distanceType models.DistanceType) (string, error) { return s.pbRepo.CalculatePace(timeStr, distanceType) }