// handlers/user.go package handlers import ( "bytes" "encoding/json" "io" "net/http" "time" "api_bb/internal/models" "api_bb/internal/service" "api_bb/pkg/logger" "api_bb/pkg/middleware" "api_bb/pkg/utils" "go.uber.org/zap" ) type UserHandler struct { logger logger.LoggerInterface userService service.UserService } func NewUserHandler(userService service.UserService) *UserHandler { return &UserHandler{ logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "user"))), userService: userService, } } type UserResponse struct { ID uint `json:"id"` Email string `json:"email"` FirstName string `json:"firstName"` LastName string `json:"lastName"` Avatar string `json:"avatar"` Phone string `json:"phone"` Experience string `json:"experience"` Goals string `json:"goals"` Newsletter bool `json:"newsletter"` Role string `json:"role"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // GetUsers возвращает список всех пользователей func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling get users request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Получаем пользователя из контекста для проверки аутентификации _, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("get users failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } // Получаем список пользователей из сервиса users, err := h.userService.GetAllUsers() if err != nil { h.logger.Error("failed to get users from service", zap.Error(err)) utils.RespondWithError(w, http.StatusInternalServerError, "Failed to get users: "+err.Error()) return } // Преобразуем в response формат var userResponses []UserResponse for _, user := range users { userResponses = append(userResponses, toUserResponse(&user)) } h.logger.Info("users list retrieved successfully", zap.Int("users_count", len(userResponses)), ) utils.RespondWithJSON(w, http.StatusOK, userResponses) } func (h *UserHandler) GetProfile(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling get profile request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("get profile failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } h.logger.Info("profile retrieved successfully", zap.Uint("user_id", user.ID), zap.String("email", user.Email), zap.String("avatar", user.Avatar), ) utils.RespondWithJSON(w, http.StatusOK, toUserResponse(user)) } type UpdateProfileRequest struct { FirstName string `json:"firstName"` LastName string `json:"lastName"` Phone string `json:"phone"` Experience string `json:"experience"` Goals string `json:"goals"` Newsletter bool `json:"newsletter"` } func (h *UserHandler) UpdateProfile(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling update profile request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) // Логируем тело запроса для отладки bodyBytes, err := io.ReadAll(r.Body) if err != nil { h.logger.Error("failed to read request body", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Failed to read request body: "+err.Error()) return } // Восстанавливаем тело для дальнейшего использования r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) h.logger.Debug("raw request body", zap.String("body", string(bodyBytes))) // Получаем пользователя из контекста currentUser, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("update profile failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required") return } var req UpdateProfileRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { h.logger.Error("failed to decode JSON payload", zap.Error(err)) utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error()) return } // Валидация обязательных полей if req.FirstName == "" { h.logger.Warn("update profile failed - first name required") utils.RespondWithError(w, http.StatusBadRequest, "First name is required") return } if req.LastName == "" { h.logger.Warn("update profile failed - last name required") utils.RespondWithError(w, http.StatusBadRequest, "Last name is required") return } h.logger.Info("updating user profile", zap.Uint("user_id", currentUser.ID), zap.String("first_name", req.FirstName), zap.String("last_name", req.LastName), zap.String("experience", req.Experience), zap.String("goals", req.Goals), zap.Bool("newsletter", req.Newsletter), ) // Обновляем данные пользователя updatedUser := &models.User{ ID: currentUser.ID, FirstName: req.FirstName, LastName: req.LastName, Phone: req.Phone, Experience: req.Experience, Goals: req.Goals, Newsletter: req.Newsletter, UpdatedAt: time.Now(), } // Сохраняем обновленные данные if err := h.userService.UpdateProfile(updatedUser); err != nil { h.logger.Error("failed to update profile in service", zap.Uint("user_id", currentUser.ID), zap.Error(err), ) utils.RespondWithError(w, http.StatusInternalServerError, "Failed to update profile: "+err.Error()) return } h.logger.Info("profile updated successfully", zap.Uint("user_id", currentUser.ID), ) utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{ "message": "Profile updated successfully", "user": toUserResponse(updatedUser), }) }