last
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"api_yal/internal/logger"
|
"api_yal/internal/logger"
|
||||||
|
"api_yal/internal/middleware"
|
||||||
"api_yal/internal/models"
|
"api_yal/internal/models"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
@@ -24,21 +25,22 @@ func NewFeedbackHandler(service FeedbackService) *FeedbackHandler {
|
|||||||
|
|
||||||
// CreateFeedback создает новый отзыв
|
// CreateFeedback создает новый отзыв
|
||||||
func (h *FeedbackHandler) CreateFeedback(w http.ResponseWriter, r *http.Request) {
|
func (h *FeedbackHandler) CreateFeedback(w http.ResponseWriter, r *http.Request) {
|
||||||
var feedback models.Feedback
|
var req CreateFeedbackRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&feedback); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.service.Create(r.Context(), &feedback); err != nil {
|
response, err := h.service.Create(r.Context(), &req)
|
||||||
|
if err != nil {
|
||||||
logger.Get().Error("Failed to create feedback", zap.Error(err))
|
logger.Get().Error("Failed to create feedback", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
json.NewEncoder(w).Encode(feedback)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeedbackByID возвращает отзыв по ID
|
// GetFeedbackByID возвращает отзыв по ID
|
||||||
@@ -50,7 +52,7 @@ func (h *FeedbackHandler) GetFeedbackByID(w http.ResponseWriter, r *http.Request
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
feedback, err := h.service.GetByID(r.Context(), uint(id))
|
response, err := h.service.GetByID(r.Context(), uint(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Get().Error("Failed to get feedback", zap.Error(err))
|
logger.Get().Error("Failed to get feedback", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
@@ -58,7 +60,7 @@ func (h *FeedbackHandler) GetFeedbackByID(w http.ResponseWriter, r *http.Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(feedback)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateFeedback обновляет существующий отзыв
|
// UpdateFeedback обновляет существующий отзыв
|
||||||
@@ -70,21 +72,21 @@ func (h *FeedbackHandler) UpdateFeedback(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var feedback models.Feedback
|
var req UpdateFeedbackRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&feedback); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
feedback.ID = uint(id)
|
|
||||||
|
|
||||||
if err := h.service.Update(r.Context(), &feedback); err != nil {
|
response, err := h.service.Update(r.Context(), uint(id), &req)
|
||||||
|
if err != nil {
|
||||||
logger.Get().Error("Failed to update feedback", zap.Error(err))
|
logger.Get().Error("Failed to update feedback", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(feedback)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteFeedback удаляет отзыв
|
// DeleteFeedback удаляет отзыв
|
||||||
@@ -98,7 +100,7 @@ func (h *FeedbackHandler) DeleteFeedback(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
if err := h.service.Delete(r.Context(), uint(id)); err != nil {
|
if err := h.service.Delete(r.Context(), uint(id)); err != nil {
|
||||||
logger.Get().Error("Failed to delete feedback", zap.Error(err))
|
logger.Get().Error("Failed to delete feedback", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +122,7 @@ func (h *FeedbackHandler) ListFeedbacks(w http.ResponseWriter, r *http.Request)
|
|||||||
pageSize = 100
|
pageSize = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
feedbacks, total, err := h.service.List(r.Context(), page, pageSize)
|
response, err := h.service.List(r.Context(), page, pageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Get().Error("Failed to list feedbacks", zap.Error(err))
|
logger.Get().Error("Failed to list feedbacks", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
@@ -128,15 +130,16 @@ func (h *FeedbackHandler) ListFeedbacks(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Header().Set("X-Total-Count", strconv.FormatInt(total, 10))
|
json.NewEncoder(w).Encode(response)
|
||||||
json.NewEncoder(w).Encode(feedbacks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMyFeedbacks возвращает отзывы текущего пользователя
|
// GetMyFeedbacks возвращает отзывы текущего пользователя
|
||||||
func (h *FeedbackHandler) GetMyFeedbacks(w http.ResponseWriter, r *http.Request) {
|
func (h *FeedbackHandler) GetMyFeedbacks(w http.ResponseWriter, r *http.Request) {
|
||||||
// Здесь нужно получить ownerID из JWT токена
|
userID, ok := middleware.GetUserID(r.Context())
|
||||||
// Для примера используем заглушку
|
if !ok {
|
||||||
ownerID := uint(1) // TODO: Get from context
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
||||||
@@ -147,8 +150,11 @@ func (h *FeedbackHandler) GetMyFeedbacks(w http.ResponseWriter, r *http.Request)
|
|||||||
if pageSize <= 0 {
|
if pageSize <= 0 {
|
||||||
pageSize = 10
|
pageSize = 10
|
||||||
}
|
}
|
||||||
|
if pageSize > 100 {
|
||||||
|
pageSize = 100
|
||||||
|
}
|
||||||
|
|
||||||
feedbacks, err := h.service.ListByOwner(r.Context(), ownerID, page, pageSize)
|
response, err := h.service.ListByOwner(r.Context(), userID, page, pageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Get().Error("Failed to get user feedbacks", zap.Error(err))
|
logger.Get().Error("Failed to get user feedbacks", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
@@ -156,7 +162,7 @@ func (h *FeedbackHandler) GetMyFeedbacks(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(feedbacks)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeedbacksByObject возвращает отзывы по объекту
|
// GetFeedbacksByObject возвращает отзывы по объекту
|
||||||
@@ -170,8 +176,18 @@ func (h *FeedbackHandler) GetFeedbacksByObject(w http.ResponseWriter, r *http.Re
|
|||||||
|
|
||||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
||||||
|
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
if pageSize > 100 {
|
||||||
|
pageSize = 100
|
||||||
|
}
|
||||||
|
|
||||||
feedbacks, err := h.service.ListByObject(r.Context(), uint(objectID), page, pageSize)
|
response, err := h.service.ListByObject(r.Context(), uint(objectID), page, pageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Get().Error("Failed to get object feedbacks", zap.Error(err))
|
logger.Get().Error("Failed to get object feedbacks", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
@@ -179,7 +195,7 @@ func (h *FeedbackHandler) GetFeedbacksByObject(w http.ResponseWriter, r *http.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(feedbacks)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeedbacksByPlatform возвращает отзывы по платформе
|
// GetFeedbacksByPlatform возвращает отзывы по платформе
|
||||||
@@ -189,8 +205,18 @@ func (h *FeedbackHandler) GetFeedbacksByPlatform(w http.ResponseWriter, r *http.
|
|||||||
|
|
||||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
||||||
|
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
if pageSize > 100 {
|
||||||
|
pageSize = 100
|
||||||
|
}
|
||||||
|
|
||||||
feedbacks, err := h.service.ListByPlatform(r.Context(), platform, page, pageSize)
|
response, err := h.service.ListByPlatform(r.Context(), platform, page, pageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Get().Error("Failed to get platform feedbacks", zap.Error(err))
|
logger.Get().Error("Failed to get platform feedbacks", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
@@ -198,7 +224,7 @@ func (h *FeedbackHandler) GetFeedbacksByPlatform(w http.ResponseWriter, r *http.
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(feedbacks)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchFeedbacks ищет отзывы по тексту
|
// SearchFeedbacks ищет отзывы по тексту
|
||||||
@@ -211,8 +237,18 @@ func (h *FeedbackHandler) SearchFeedbacks(w http.ResponseWriter, r *http.Request
|
|||||||
|
|
||||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
||||||
|
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
if pageSize > 100 {
|
||||||
|
pageSize = 100
|
||||||
|
}
|
||||||
|
|
||||||
feedbacks, err := h.service.Search(r.Context(), query, page, pageSize)
|
response, err := h.service.Search(r.Context(), query, page, pageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Get().Error("Failed to search feedbacks", zap.Error(err))
|
logger.Get().Error("Failed to search feedbacks", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
@@ -220,7 +256,20 @@ func (h *FeedbackHandler) SearchFeedbacks(w http.ResponseWriter, r *http.Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(feedbacks)
|
json.NewEncoder(w).Encode(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFeedbackStats возвращает статистику по отзывам
|
||||||
|
func (h *FeedbackHandler) GetFeedbackStats(w http.ResponseWriter, r *http.Request) {
|
||||||
|
stats, err := h.service.GetStats(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
logger.Get().Error("Failed to get feedback stats", zap.Error(err))
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeedbackComments возвращает комментарии к отзыву
|
// GetFeedbackComments возвращает комментарии к отзыву
|
||||||
@@ -234,16 +283,34 @@ func (h *FeedbackHandler) GetFeedbackComments(w http.ResponseWriter, r *http.Req
|
|||||||
|
|
||||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
|
||||||
|
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
if pageSize > 100 {
|
||||||
|
pageSize = 100
|
||||||
|
}
|
||||||
|
|
||||||
comments, err := h.service.GetComments(r.Context(), uint(id), page, pageSize)
|
comments, total, err := h.service.GetComments(r.Context(), uint(id), page, pageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Get().Error("Failed to get comments", zap.Error(err))
|
logger.Get().Error("Failed to get comments", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"items": comments,
|
||||||
|
"total": total,
|
||||||
|
"page": page,
|
||||||
|
"page_size": pageSize,
|
||||||
|
"total_pages": int((total + int64(pageSize) - 1) / int64(pageSize)),
|
||||||
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(comments)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddComment добавляет комментарий к отзыву
|
// AddComment добавляет комментарий к отзыву
|
||||||
@@ -255,15 +322,18 @@ func (h *FeedbackHandler) AddComment(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var comment models.Comment
|
var req struct {
|
||||||
if err := json.NewDecoder(r.Body).Decode(&comment); err != nil {
|
Text string `json:"text" binding:"required"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.service.AddComment(r.Context(), uint(id), &comment); err != nil {
|
comment, err := h.service.AddComment(r.Context(), uint(id), req.Text)
|
||||||
|
if err != nil {
|
||||||
logger.Get().Error("Failed to add comment", zap.Error(err))
|
logger.Get().Error("Failed to add comment", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +352,7 @@ func (h *FeedbackHandler) UpdateComment(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var req struct {
|
var req struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text" binding:"required"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||||
@@ -291,7 +361,7 @@ func (h *FeedbackHandler) UpdateComment(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
if err := h.service.UpdateComment(r.Context(), uint(commentID), req.Text); err != nil {
|
if err := h.service.UpdateComment(r.Context(), uint(commentID), req.Text); err != nil {
|
||||||
logger.Get().Error("Failed to update comment", zap.Error(err))
|
logger.Get().Error("Failed to update comment", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +380,7 @@ func (h *FeedbackHandler) DeleteComment(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
if err := h.service.DeleteComment(r.Context(), uint(commentID)); err != nil {
|
if err := h.service.DeleteComment(r.Context(), uint(commentID)); err != nil {
|
||||||
logger.Get().Error("Failed to delete comment", zap.Error(err))
|
logger.Get().Error("Failed to delete comment", zap.Error(err))
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -579,4 +579,171 @@ func (s *feedbackServiceImpl) isValidPlatform(platform models.PlatformType) bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавьте в конец файла service.go:
|
||||||
|
|
||||||
|
// AddComment добавляет комментарий к отзыву
|
||||||
|
func (s *feedbackServiceImpl) AddComment(ctx context.Context, feedbackID uint, text string) (*models.Comment, error) {
|
||||||
|
if feedbackID == 0 {
|
||||||
|
return nil, errors.New("invalid feedback ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(text) == "" {
|
||||||
|
return nil, errors.New("comment text cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(text) > 1000 {
|
||||||
|
return nil, errors.New("comment text cannot exceed 1000 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем существование отзыва
|
||||||
|
_, err := s.feedbackRepository.GetByID(feedbackID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, fmt.Errorf("feedback with ID %d not found", feedbackID)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to get feedback: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем userID из контекста
|
||||||
|
userID, ok := ctx.Value(middleware.UserIDKey).(uint)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
comment := &models.Comment{
|
||||||
|
FeedbackID: feedbackID,
|
||||||
|
UserID: userID,
|
||||||
|
Text: text,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.feedbackRepository.CreateComment(comment); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create comment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем счетчик комментариев
|
||||||
|
if err := s.updateCommentCount(feedbackID); err != nil {
|
||||||
|
// Логируем ошибку, но не возвращаем, так как комментарий уже создан
|
||||||
|
_ = err
|
||||||
|
}
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetComments возвращает комментарии к отзыву
|
||||||
|
func (s *feedbackServiceImpl) GetComments(ctx context.Context, feedbackID uint, page, pageSize int) ([]models.Comment, int64, error) {
|
||||||
|
if feedbackID == 0 {
|
||||||
|
return nil, 0, errors.New("invalid feedback ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем существование отзыва
|
||||||
|
_, err := s.feedbackRepository.GetByID(feedbackID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, 0, fmt.Errorf("feedback with ID %d not found", feedbackID)
|
||||||
|
}
|
||||||
|
return nil, 0, fmt.Errorf("failed to get feedback: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset, limit := s.normalizePagination(page, pageSize)
|
||||||
|
|
||||||
|
comments, err := s.feedbackRepository.GetComments(feedbackID, offset, limit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to get comments: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := s.feedbackRepository.GetCommentCount(feedbackID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to count comments: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return comments, int64(total), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateComment обновляет комментарий
|
||||||
|
func (s *feedbackServiceImpl) UpdateComment(ctx context.Context, commentID uint, text string) error {
|
||||||
|
if commentID == 0 {
|
||||||
|
return errors.New("invalid comment ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(text) == "" {
|
||||||
|
return errors.New("comment text cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(text) > 1000 {
|
||||||
|
return errors.New("comment text cannot exceed 1000 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err := s.feedbackRepository.GetCommentByID(commentID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fmt.Errorf("comment with ID %d not found", commentID)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to get comment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что текущий пользователь является владельцем
|
||||||
|
userID, ok := ctx.Value(middleware.UserIDKey).(uint)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if comment.UserID != userID {
|
||||||
|
return errors.New("unauthorized: cannot update comment owned by another user")
|
||||||
|
}
|
||||||
|
|
||||||
|
comment.Text = text
|
||||||
|
if err := s.feedbackRepository.UpdateComment(comment); err != nil {
|
||||||
|
return fmt.Errorf("failed to update comment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteComment удаляет комментарий
|
||||||
|
func (s *feedbackServiceImpl) DeleteComment(ctx context.Context, commentID uint) error {
|
||||||
|
if commentID == 0 {
|
||||||
|
return errors.New("invalid comment ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err := s.feedbackRepository.GetCommentByID(commentID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fmt.Errorf("comment with ID %d not found", commentID)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to get comment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что текущий пользователь является владельцем
|
||||||
|
userID, ok := ctx.Value(middleware.UserIDKey).(uint)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if comment.UserID != userID {
|
||||||
|
return errors.New("unauthorized: cannot delete comment owned by another user")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.feedbackRepository.DeleteComment(commentID); err != nil {
|
||||||
|
return fmt.Errorf("failed to delete comment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем счетчик комментариев
|
||||||
|
if err := s.updateCommentCount(comment.FeedbackID); err != nil {
|
||||||
|
// Логируем ошибку, но не возвращаем
|
||||||
|
_ = err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateCommentCount обновляет счетчик комментариев у отзыва
|
||||||
|
func (s *feedbackServiceImpl) updateCommentCount(feedbackID uint) error {
|
||||||
|
count, err := s.feedbackRepository.GetCommentCount(feedbackID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.feedbackRepository.UpdateCommentCount(feedbackID, count)
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// FeedbackRepository интерфейс для операций с моделью Feedback
|
// FeedbackRepository интерфейс для операций с моделью Feedback
|
||||||
|
//
|
||||||
//go:generate mockgen -destination=mocks/feedback_repository.go -package=mocks . FeedbackRepository
|
//go:generate mockgen -destination=mocks/feedback_repository.go -package=mocks . FeedbackRepository
|
||||||
type FeedbackRepository interface {
|
type FeedbackRepository interface {
|
||||||
// Create создает новый отзыв
|
// Create создает новый отзыв
|
||||||
@@ -51,4 +52,26 @@ type FeedbackRepository interface {
|
|||||||
|
|
||||||
// Search находит отзывы по тексту
|
// Search находит отзывы по тексту
|
||||||
Search(query string, offset, limit int) ([]models.Feedback, error)
|
Search(query string, offset, limit int) ([]models.Feedback, error)
|
||||||
}
|
|
||||||
|
// Добавьте в интерфейс FeedbackRepository:
|
||||||
|
|
||||||
|
// Comment methods
|
||||||
|
CreateComment(comment *models.Comment) error
|
||||||
|
GetCommentByID(id uint) (*models.Comment, error)
|
||||||
|
UpdateComment(comment *models.Comment) error
|
||||||
|
DeleteComment(id uint) error
|
||||||
|
|
||||||
|
// Additional methods for stats
|
||||||
|
GetAverageScore() (float64, error)
|
||||||
|
GetPlatformStats() (map[models.PlatformType]int, error)
|
||||||
|
GetScoreDistribution() (map[int]int, error)
|
||||||
|
|
||||||
|
// Count methods
|
||||||
|
CountByOwner(ownerID uint) (int64, error)
|
||||||
|
CountByObject(objectID uint) (int64, error)
|
||||||
|
CountByPlatform(platform models.PlatformType) (int64, error)
|
||||||
|
CountBySearch(query string) (int64, error)
|
||||||
|
|
||||||
|
// GetObject by ID (general method)
|
||||||
|
GetObjectByID(id uint) (*models.Object, error)
|
||||||
|
}
|
||||||
|
|||||||
@@ -156,4 +156,128 @@ func (r *feedbackRepositoryImpl) getObjectByID(id uint) (*models.Object, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &object, nil
|
return &object, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateComment создает новый комментарий
|
||||||
|
func (r *feedbackRepositoryImpl) CreateComment(comment *models.Comment) error {
|
||||||
|
return r.db.Create(comment).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommentByID возвращает комментарий по ID
|
||||||
|
func (r *feedbackRepositoryImpl) GetCommentByID(id uint) (*models.Comment, error) {
|
||||||
|
var comment models.Comment
|
||||||
|
err := r.db.First(&comment, id).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateComment обновляет комментарий
|
||||||
|
func (r *feedbackRepositoryImpl) UpdateComment(comment *models.Comment) error {
|
||||||
|
return r.db.Save(comment).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteComment удаляет комментарий
|
||||||
|
func (r *feedbackRepositoryImpl) DeleteComment(id uint) error {
|
||||||
|
return r.db.Delete(&models.Comment{}, id).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAverageScore возвращает средний балл отзывов
|
||||||
|
func (r *feedbackRepositoryImpl) GetAverageScore() (float64, error) {
|
||||||
|
var avg float64
|
||||||
|
err := r.db.Model(&models.Feedback{}).Select("COALESCE(AVG(score), 0)").Scan(&avg).Error
|
||||||
|
return avg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPlatformStats возвращает статистику по платформам
|
||||||
|
func (r *feedbackRepositoryImpl) GetPlatformStats() (map[models.PlatformType]int, error) {
|
||||||
|
type Result struct {
|
||||||
|
Platform models.PlatformType
|
||||||
|
Count int
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []Result
|
||||||
|
err := r.db.Model(&models.Feedback{}).
|
||||||
|
Select("platform, COUNT(*) as count").
|
||||||
|
Group("platform").
|
||||||
|
Scan(&results).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := make(map[models.PlatformType]int)
|
||||||
|
for _, r := range results {
|
||||||
|
stats[r.Platform] = r.Count
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScoreDistribution возвращает распределение оценок
|
||||||
|
func (r *feedbackRepositoryImpl) GetScoreDistribution() (map[int]int, error) {
|
||||||
|
type Result struct {
|
||||||
|
Score int
|
||||||
|
Count int
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []Result
|
||||||
|
err := r.db.Model(&models.Feedback{}).
|
||||||
|
Select("score, COUNT(*) as count").
|
||||||
|
Group("score").
|
||||||
|
Scan(&results).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
distribution := make(map[int]int)
|
||||||
|
for i := 1; i <= 5; i++ {
|
||||||
|
distribution[i] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range results {
|
||||||
|
distribution[r.Score] = r.Count
|
||||||
|
}
|
||||||
|
|
||||||
|
return distribution, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountByOwner возвращает количество отзывов владельца
|
||||||
|
func (r *feedbackRepositoryImpl) CountByOwner(ownerID uint) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&models.Feedback{}).Where("owner_id = ?", ownerID).Count(&count).Error
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountByObject возвращает количество отзывов объекта
|
||||||
|
func (r *feedbackRepositoryImpl) CountByObject(objectID uint) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&models.Feedback{}).Where("object_id = ?", objectID).Count(&count).Error
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountByPlatform возвращает количество отзывов платформы
|
||||||
|
func (r *feedbackRepositoryImpl) CountByPlatform(platform models.PlatformType) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&models.Feedback{}).Where("platform = ?", platform).Count(&count).Error
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountBySearch возвращает количество отзывов по поисковому запросу
|
||||||
|
func (r *feedbackRepositoryImpl) CountBySearch(query string) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&models.Feedback{}).Where("text LIKE ?", "%"+query+"%").Count(&count).Error
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetObjectByID возвращает объект по ID
|
||||||
|
func (r *feedbackRepositoryImpl) GetObjectByID(id uint) (*models.Object, error) {
|
||||||
|
var object models.Object
|
||||||
|
err := r.db.First(&object, id).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &object, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user