modified: serv_nginx/api_bb/internal/handlers/handlers.go
new file: serv_nginx/api_bb/internal/handlers/user_stats_handler.go modified: serv_nginx/api_bb/internal/routes/routes.go modified: serv_nginx/api_bb/internal/service/user_stats_service.go add EndPoints for user stats
This commit is contained in:
@@ -1,45 +1,258 @@
|
||||
// services/user_stats_service.go
|
||||
// service/user_stats_service.go
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"api_bb/internal/models"
|
||||
"api_bb/internal/repository"
|
||||
"api_bb/pkg/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type UserStatsService struct {
|
||||
userStatsRepo repository.UserStatsRepository
|
||||
type UserStatsService interface {
|
||||
GetUserStats(userID uint) (*models.UserStatsResponse, error)
|
||||
UpdatePersonalBest(userID uint, distanceType string, time string) error
|
||||
IncrementWorkout(userID uint, distance float64, duration int) error
|
||||
ResetWeeklyDistance(userID uint) error
|
||||
ResetMonthlyDistance(userID uint) error
|
||||
CreateUserStats(userID uint) error
|
||||
}
|
||||
|
||||
func NewUserStatsService(userStatsRepo repository.UserStatsRepository) *UserStatsService {
|
||||
return &UserStatsService{
|
||||
type userStatsService struct {
|
||||
logger logger.LoggerInterface
|
||||
userStatsRepo repository.UserStatsRepository
|
||||
}
|
||||
|
||||
func NewUserStatsService(userStatsRepo repository.UserStatsRepository) UserStatsService {
|
||||
return &userStatsService{
|
||||
logger: logger.NewWrapper(logger.Get().With(zap.String("service", "user_stats"))),
|
||||
userStatsRepo: userStatsRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserStatsService) AddWorkout(userID uint, distance float64, duration int, workoutTime time.Time) error {
|
||||
// Обновляем общую статистику
|
||||
if err := s.userStatsRepo.IncrementWorkouts(userID, distance, duration); err != nil {
|
||||
// GetUserStats возвращает статистику пользователя в формате DTO
|
||||
func (s *userStatsService) GetUserStats(userID uint) (*models.UserStatsResponse, error) {
|
||||
s.logger.Info("getting user stats",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
|
||||
stats, err := s.userStatsRepo.GetUserStatsResponse(userID)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get user stats from repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.logger.Debug("user stats retrieved successfully",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Float64("total_distance", stats.TotalDistance),
|
||||
zap.Int("workouts_count", stats.WorkoutsCount),
|
||||
)
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// UpdatePersonalBest обновляет личный рекорд пользователя
|
||||
func (s *userStatsService) UpdatePersonalBest(userID uint, distanceType string, time string) error {
|
||||
s.logger.Info("updating personal best",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.String("distance_type", distanceType),
|
||||
zap.String("time", time),
|
||||
)
|
||||
|
||||
// Проверяем существование статистики пользователя
|
||||
_, err := s.userStatsRepo.GetByUserID(userID)
|
||||
if err != nil {
|
||||
s.logger.Warn("user stats not found, creating new stats",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
if err := s.CreateUserStats(userID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.userStatsRepo.UpdatePersonalBest(userID, distanceType, time); err != nil {
|
||||
s.logger.Error("failed to update personal best in repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.String("distance_type", distanceType),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// Обновляем серии
|
||||
if err := s.userStatsRepo.UpdateStreaks(userID, workoutTime); err != nil {
|
||||
s.logger.Info("personal best updated successfully",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.String("distance_type", distanceType),
|
||||
zap.String("time", time),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncrementWorkout увеличивает счетчик тренировок и обновляет статистику
|
||||
func (s *userStatsService) IncrementWorkout(userID uint, distance float64, duration int) error {
|
||||
s.logger.Info("incrementing workout stats",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Float64("distance", distance),
|
||||
zap.Int("duration", duration),
|
||||
)
|
||||
|
||||
// Проверяем существование статистики пользователя
|
||||
_, err := s.userStatsRepo.GetByUserID(userID)
|
||||
if err != nil {
|
||||
s.logger.Warn("user stats not found, creating new stats",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
if err := s.CreateUserStats(userID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем серии тренировок
|
||||
currentTime := time.Now()
|
||||
if err := s.userStatsRepo.UpdateStreaks(userID, currentTime); err != nil {
|
||||
s.logger.Error("failed to update streaks in repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// Обновляем недельный и месячный пробег
|
||||
if err := s.userStatsRepo.UpdateWeeklyDistance(userID, distance); err != nil {
|
||||
s.logger.Error("failed to update weekly distance in repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return s.userStatsRepo.UpdateMonthlyDistance(userID, distance)
|
||||
if err := s.userStatsRepo.UpdateMonthlyDistance(userID, distance); err != nil {
|
||||
s.logger.Error("failed to update monthly distance in repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// Увеличиваем счетчик тренировок и обновляем общие показатели
|
||||
if err := s.userStatsRepo.IncrementWorkouts(userID, distance, duration); err != nil {
|
||||
s.logger.Error("failed to increment workouts in repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
s.logger.Info("workout stats incremented successfully",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Float64("distance", distance),
|
||||
zap.Int("duration", duration),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *UserStatsService) GetUserStats(userID uint) (*models.UserStatsResponse, error) {
|
||||
return s.userStatsRepo.GetUserStatsResponse(userID)
|
||||
// ResetWeeklyDistance сбрасывает недельный пробег
|
||||
func (s *userStatsService) ResetWeeklyDistance(userID uint) error {
|
||||
s.logger.Info("resetting weekly distance",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
|
||||
userStats, err := s.userStatsRepo.GetByUserID(userID)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get user stats for weekly reset",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
userStats.WeeklyDistance = 0
|
||||
if err := s.userStatsRepo.Update(userStats); err != nil {
|
||||
s.logger.Error("failed to reset weekly distance in repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
s.logger.Info("weekly distance reset successfully",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *UserStatsService) UpdatePersonalBest(userID uint, distanceType string, time string) error {
|
||||
return s.userStatsRepo.UpdatePersonalBest(userID, distanceType, time)
|
||||
// ResetMonthlyDistance сбрасывает месячный пробег
|
||||
func (s *userStatsService) ResetMonthlyDistance(userID uint) error {
|
||||
s.logger.Info("resetting monthly distance",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
|
||||
userStats, err := s.userStatsRepo.GetByUserID(userID)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get user stats for monthly reset",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
userStats.MonthlyDistance = 0
|
||||
if err := s.userStatsRepo.Update(userStats); err != nil {
|
||||
s.logger.Error("failed to reset monthly distance in repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
s.logger.Info("monthly distance reset successfully",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateUserStats создает начальную статистику для пользователя
|
||||
func (s *userStatsService) CreateUserStats(userID uint) error {
|
||||
s.logger.Info("creating user stats",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
|
||||
userStats := &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 := s.userStatsRepo.Create(userStats); err != nil {
|
||||
s.logger.Error("failed to create user stats in repository",
|
||||
zap.Uint("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
s.logger.Info("user stats created successfully",
|
||||
zap.Uint("user_id", userID),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user