diff --git a/serv_nginx/api_bb/internal/repository/user_stats_repository.go b/serv_nginx/api_bb/internal/repository/user_stats_repository.go index 3b9872b..6b90f91 100644 --- a/serv_nginx/api_bb/internal/repository/user_stats_repository.go +++ b/serv_nginx/api_bb/internal/repository/user_stats_repository.go @@ -4,9 +4,9 @@ package repository import ( "time" - "gorm.io/gorm" "api_bb/internal/models" "api_bb/pkg/utils" + "gorm.io/gorm" ) type UserStatsRepository interface { @@ -21,6 +21,7 @@ type UserStatsRepository interface { IncrementWorkouts(userID uint, distance float64, duration int) error UpdatePersonalBest(userID uint, distanceType string, time string) error GetUserStatsResponse(userID uint) (*models.UserStatsResponse, error) + GetByUserIDOrCreate(userID uint) (*models.UserStats, error) } type userStatsRepository struct { @@ -31,6 +32,39 @@ func NewUserStatsRepository(db *gorm.DB) UserStatsRepository { return &userStatsRepository{db: db} } +// GetByUserIDOrCreate возвращает статистику по ID пользователя или создает новую +func (r *userStatsRepository) GetByUserIDOrCreate(userID uint) (*models.UserStats, error) { + userStats, err := r.GetByUserID(userID) + if err != nil { + if err == gorm.ErrRecordNotFound { + // Создаем новую статистику + newStats := &models.UserStats{ + UserID: userID, + TotalDistance: 0, + TotalTime: 0, + AvgPace: "0:00", + WorkoutsCount: 0, + CurrentStreak: 0, + LongestStreak: 0, + WeeklyDistance: 0, + MonthlyDistance: 0, + Best5K: "", + Best10K: "", + BestHalf: "", + BestMarathon: "", + LastWorkout: time.Time{}, + } + + if err := r.Create(newStats); err != nil { + return nil, err + } + return newStats, nil + } + return nil, err + } + return userStats, nil +} + // Create создает новую статистику пользователя func (r *userStatsRepository) Create(userStats *models.UserStats) error { return r.db.Create(userStats).Error @@ -156,7 +190,7 @@ func (r *userStatsRepository) UpdatePersonalBest(userID uint, distanceType strin // GetUserStatsResponse возвращает статистику в формате DTO func (r *userStatsRepository) GetUserStatsResponse(userID uint) (*models.UserStatsResponse, error) { - userStats, err := r.GetByUserID(userID) + userStats, err := r.GetByUserIDOrCreate(userID) if err != nil { return nil, err } @@ -178,4 +212,3 @@ func (r *userStatsRepository) GetUserStatsResponse(userID uint) (*models.UserSta }, }, nil } - diff --git a/serv_nginx/api_bb/internal/scripts/migrate_existing_users.go b/serv_nginx/api_bb/internal/scripts/migrate_existing_users.go new file mode 100644 index 0000000..5a99176 --- /dev/null +++ b/serv_nginx/api_bb/internal/scripts/migrate_existing_users.go @@ -0,0 +1,74 @@ +// scripts/migrate_existing_users.go +package main + +import ( + "api_bb/internal/models" + "api_bb/internal/repository" + "api_bb/pkg/logger" + + "gorm.io/gorm" + "go.uber.org/zap" +) + +func MigrateExistingUsers(db *gorm.DB) error { + log := logger.NewWrapper(logger.Get().With(zap.String("script", "migrate_existing_users"))) + + userRepo := repository.NewUserRepository(db) + userStatsRepo := repository.NewUserStatsRepository(db) + + // Получаем всех пользователей + users, err := userRepo.FindAll() + if err != nil { + return err + } + + log.Info("starting migration for existing users", + zap.Int("total_users", len(users))) + + successCount := 0 + for _, user := range users { + // Проверяем, есть ли уже статистика + _, err := userStatsRepo.GetByUserID(user.ID) + if err == gorm.ErrRecordNotFound { + // Создаем статистику + userStats := &models.UserStats{ + UserID: user.ID, + TotalDistance: 0, + TotalTime: 0, + AvgPace: "0:00", + WorkoutsCount: 0, + CurrentStreak: 0, + LongestStreak: 0, + WeeklyDistance: 0, + MonthlyDistance: 0, + Best5K: "", + Best10K: "", + BestHalf: "", + BestMarathon: "", + LastWorkout: user.CreatedAt, // Используем дату создания как последнюю тренировку + } + + if err := userStatsRepo.Create(userStats); err != nil { + log.Error("failed to create stats for user", + zap.Uint("user_id", user.ID), + zap.Error(err)) + continue + } + + successCount++ + log.Info("created stats for user", + zap.Uint("user_id", user.ID), + zap.String("email", user.Email)) + } else if err != nil { + log.Error("error checking stats for user", + zap.Uint("user_id", user.ID), + zap.Error(err)) + } + } + + log.Info("migration completed", + zap.Int("successful_creations", successCount), + zap.Int("total_users", len(users))) + + return nil +} \ No newline at end of file diff --git a/serv_nginx/api_bb/internal/service/auth_service.go b/serv_nginx/api_bb/internal/service/auth_service.go index 82320ad..25690f7 100644 --- a/serv_nginx/api_bb/internal/service/auth_service.go +++ b/serv_nginx/api_bb/internal/service/auth_service.go @@ -55,12 +55,14 @@ func (s *authService) Register(user *models.User) error { ) return err } + s.logger.Info("User registered successfully", zap.Uint("user_id", user.ID), zap.String("email", user.Email), ) + return nil } diff --git a/serv_nginx/api_bb/internal/service/user_stats_service.go b/serv_nginx/api_bb/internal/service/user_stats_service.go index c388184..9d6d349 100644 --- a/serv_nginx/api_bb/internal/service/user_stats_service.go +++ b/serv_nginx/api_bb/internal/service/user_stats_service.go @@ -21,8 +21,8 @@ type UserStatsService interface { } type userStatsService struct { - logger logger.LoggerInterface - userStatsRepo repository.UserStatsRepository + logger logger.LoggerInterface + userStatsRepo repository.UserStatsRepository } func NewUserStatsService(userStatsRepo repository.UserStatsRepository) UserStatsService { @@ -64,15 +64,14 @@ func (s *userStatsService) UpdatePersonalBest(userID uint, distanceType string, zap.String("time", time), ) - // Проверяем существование статистики пользователя - _, err := s.userStatsRepo.GetByUserID(userID) + // Используем GetByUserIDOrCreate вместо проверки существования + _, err := s.userStatsRepo.GetByUserIDOrCreate(userID) if err != nil { - s.logger.Warn("user stats not found, creating new stats", + s.logger.Error("failed to get or create user stats", zap.Uint("user_id", userID), + zap.Error(err), ) - if err := s.CreateUserStats(userID); err != nil { - return err - } + return err } if err := s.userStatsRepo.UpdatePersonalBest(userID, distanceType, time); err != nil { @@ -101,15 +100,14 @@ func (s *userStatsService) IncrementWorkout(userID uint, distance float64, durat zap.Int("duration", duration), ) - // Проверяем существование статистики пользователя - _, err := s.userStatsRepo.GetByUserID(userID) + // Используем GetByUserIDOrCreate для гарантии существования статистики + _, err := s.userStatsRepo.GetByUserIDOrCreate(userID) if err != nil { - s.logger.Warn("user stats not found, creating new stats", + s.logger.Error("failed to get or create user stats", zap.Uint("user_id", userID), + zap.Error(err), ) - if err := s.CreateUserStats(userID); err != nil { - return err - } + return err } // Обновляем серии тренировок @@ -226,20 +224,20 @@ func (s *userStatsService) CreateUserStats(userID uint) error { ) userStats := &models.UserStats{ - UserID: userID, - TotalDistance: 0, - TotalTime: 0, - AvgPace: "0:00", - WorkoutsCount: 0, - CurrentStreak: 0, - LongestStreak: 0, - WeeklyDistance: 0, + UserID: userID, + TotalDistance: 0, + TotalTime: 0, + AvgPace: "0:00", + WorkoutsCount: 0, + CurrentStreak: 0, + LongestStreak: 0, + WeeklyDistance: 0, MonthlyDistance: 0, - Best5K: "", - Best10K: "", - BestHalf: "", - BestMarathon: "", - LastWorkout: time.Time{}, + Best5K: "", + Best10K: "", + BestHalf: "", + BestMarathon: "", + LastWorkout: time.Time{}, } if err := s.userStatsRepo.Create(userStats); err != nil { @@ -255,4 +253,4 @@ func (s *userStatsService) CreateUserStats(userID uint) error { ) return nil -} \ No newline at end of file +} diff --git a/serv_nginx/bbvue/src/views/Achievements.vue b/serv_nginx/bbvue/src/views/Achievements.vue index 2c9ecd0..e491845 100644 --- a/serv_nginx/bbvue/src/views/Achievements.vue +++ b/serv_nginx/bbvue/src/views/Achievements.vue @@ -713,7 +713,9 @@ export default { } .achievement-details { - space-y: 0.5rem; + display: flex; + flex-direction: column; + gap: 1.5rem; /* Современный способ для Flexbox */ } .pace { diff --git a/serv_nginx/bbvue/src/views/Reviews.vue b/serv_nginx/bbvue/src/views/Reviews.vue index 799c0e5..e2ed58a 100644 --- a/serv_nginx/bbvue/src/views/Reviews.vue +++ b/serv_nginx/bbvue/src/views/Reviews.vue @@ -1163,7 +1163,9 @@ export default { } .review-content { - space-y: 1rem; + display: flex; + flex-direction: column; + gap: 1.5rem; /* Современный способ для Flexbox */ } .review-text { diff --git a/serv_nginx/bbvue/src/views/Training.vue b/serv_nginx/bbvue/src/views/Training.vue index fd6cee4..c14e872 100644 --- a/serv_nginx/bbvue/src/views/Training.vue +++ b/serv_nginx/bbvue/src/views/Training.vue @@ -945,7 +945,9 @@ export default { } .signup-form { - space-y: 1.5rem; + display: flex; + flex-direction: column; + gap: 1.5rem; /* Современный способ для Flexbox */ } .form-group {