package object import ( "encoding/json" "net/http" "strconv" "api_yal/internal/middleware" "github.com/go-chi/chi/v5" ) type ObjectHandler struct { objectService ObjectService } func NewObjectHandler(objectService ObjectService) *ObjectHandler { return &ObjectHandler{ objectService: objectService, } } // GetObjectByID обрабатывает GET /objects/{id} func (h *ObjectHandler) GetObjectByID(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32) if err != nil { http.Error(w, "Invalid object ID", http.StatusBadRequest) return } response, err := h.objectService.GetObjectByID(r.Context(), uint(id)) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusOK, response) } // CreateObject обрабатывает POST /objects func (h *ObjectHandler) CreateObject(w http.ResponseWriter, r *http.Request) { var req CreateObjectRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } // Получаем ID пользователя из контекста (из AuthMiddleware) userID, ok := r.Context().Value(middleware.UserIDKey).(uint) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } // Устанавливаем owner_id из аутентифицированного пользователя req.OwnerID = userID response, err := h.objectService.CreateObject(r.Context(), &req) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusCreated, response) } // UpdateObject обрабатывает PUT /objects/{id} func (h *ObjectHandler) UpdateObject(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32) if err != nil { http.Error(w, "Invalid object ID", http.StatusBadRequest) return } var req UpdateObjectRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } response, err := h.objectService.UpdateObject(r.Context(), uint(id), &req) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusOK, response) } // DeleteObject обрабатывает DELETE /objects/{id} func (h *ObjectHandler) DeleteObject(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32) if err != nil { http.Error(w, "Invalid object ID", http.StatusBadRequest) return } if err := h.objectService.DeleteObject(r.Context(), uint(id)); err != nil { h.handleError(w, err) return } w.WriteHeader(http.StatusNoContent) } // ListObjects обрабатывает GET /objects func (h *ObjectHandler) ListObjects(w http.ResponseWriter, r *http.Request) { req := &ListObjectsRequest{ Page: h.getQueryParamInt(r, "page", 1), PageSize: h.getQueryParamInt(r, "page_size", 10), Type: r.URL.Query().Get("type"), Query: r.URL.Query().Get("q"), } if statusStr := r.URL.Query().Get("is_active"); statusStr != "" { isActive, err := strconv.ParseBool(statusStr) if err == nil { req.Status = &isActive } } response, err := h.objectService.ListObjects(r.Context(), req) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusOK, response) } // GetObjectsByOwner обрабатывает GET /objects/owner/{ownerId} func (h *ObjectHandler) GetObjectsByOwner(w http.ResponseWriter, r *http.Request) { ownerID, err := strconv.ParseUint(chi.URLParam(r, "ownerId"), 10, 32) if err != nil { http.Error(w, "Invalid owner ID", http.StatusBadRequest) return } page := h.getQueryParamInt(r, "page", 1) pageSize := h.getQueryParamInt(r, "page_size", 10) response, err := h.objectService.GetObjectsByOwner(r.Context(), uint(ownerID), page, pageSize) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusOK, response) } // SearchObjects обрабатывает GET /objects/search func (h *ObjectHandler) SearchObjects(w http.ResponseWriter, r *http.Request) { query := r.URL.Query().Get("q") if query == "" { http.Error(w, "Search query is required", http.StatusBadRequest) return } page := h.getQueryParamInt(r, "page", 1) pageSize := h.getQueryParamInt(r, "page_size", 10) response, err := h.objectService.SearchObjects(r.Context(), query, page, pageSize) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusOK, response) } // GetNearbyObjects обрабатывает GET /objects/nearby func (h *ObjectHandler) GetNearbyObjects(w http.ResponseWriter, r *http.Request) { lat, err := strconv.ParseFloat(r.URL.Query().Get("lat"), 64) if err != nil { http.Error(w, "Invalid latitude", http.StatusBadRequest) return } lng, err := strconv.ParseFloat(r.URL.Query().Get("lng"), 64) if err != nil { http.Error(w, "Invalid longitude", http.StatusBadRequest) return } radius, err := strconv.ParseFloat(r.URL.Query().Get("radius"), 64) if err != nil || radius <= 0 { radius = 1000 // По умолчанию 1 км } page := h.getQueryParamInt(r, "page", 1) pageSize := h.getQueryParamInt(r, "page_size", 10) response, err := h.objectService.GetNearbyObjects(r.Context(), lat, lng, radius, page, pageSize) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusOK, response) } // CreateFeedback обрабатывает POST /objects/{id}/feedbacks func (h *ObjectHandler) CreateFeedback(w http.ResponseWriter, r *http.Request) { objectID, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32) if err != nil { http.Error(w, "Invalid object ID", http.StatusBadRequest) return } var req CreateFeedbackRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } req.ObjectID = uint(objectID) userID, ok := r.Context().Value(middleware.UserIDKey).(uint) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } response, err := h.objectService.CreateFeedback(r.Context(), &req, userID) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusCreated, response) } // CreateRatingVote обрабатывает POST /objects/{id}/ratings func (h *ObjectHandler) CreateRatingVote(w http.ResponseWriter, r *http.Request) { objectID, err := strconv.ParseUint(chi.URLParam(r, "id"), 10, 32) if err != nil { http.Error(w, "Invalid object ID", http.StatusBadRequest) return } var req CreateRatingVoteRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } req.TargetID = uint(objectID) userID, ok := r.Context().Value(middleware.UserIDKey).(uint) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } response, err := h.objectService.CreateRatingVote(r.Context(), &req, userID) if err != nil { h.handleError(w, err) return } h.respondWithJSON(w, http.StatusCreated, response) } // Вспомогательные методы func (h *ObjectHandler) respondWithJSON(w http.ResponseWriter, status int, data interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if err := json.NewEncoder(w).Encode(data); err != nil { http.Error(w, "Failed to encode response", http.StatusInternalServerError) } } func (h *ObjectHandler) handleError(w http.ResponseWriter, err error) { switch err { case ErrObjectNotFound: http.Error(w, err.Error(), http.StatusNotFound) case ErrInvalidOwnerID, ErrShortNameRequired, ErrAlreadyVoted: http.Error(w, err.Error(), http.StatusBadRequest) case ErrNotImplemented: http.Error(w, err.Error(), http.StatusNotImplemented) default: http.Error(w, "Internal server error", http.StatusInternalServerError) } } func (h *ObjectHandler) getQueryParamInt(r *http.Request, key string, defaultValue int) int { value := r.URL.Query().Get(key) if value == "" { return defaultValue } intValue, err := strconv.Atoi(value) if err != nil { return defaultValue } return intValue }