Files
tp/main_dc/yalarba/api_yal/internal/domain/comment/handler.go
T
valitovgaziz cc3d0a8b07 On branch main
modified:   yalarba/api_yal/internal/domain/account/service.go
	modified:   yalarba/api_yal/internal/domain/comment/dto.go
	new file:   yalarba/api_yal/internal/domain/comment/handler.go
	new file:   yalarba/api_yal/internal/domain/comment/router.go
	new file:   yalarba/api_yal/internal/domain/comment/service.go
	modified:   yalarba/api_yal/internal/repository/feedback_repository.go
	new file:   yalarba/api_yal/internal/util/JSON_resp.go
Realize comment domain hole
2026-05-19 18:11:20 +05:00

438 lines
13 KiB
Go

package comment
import (
"api_yal/internal/logger"
"api_yal/internal/util"
"encoding/json"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
type CommentHandler struct {
service CommentService
logger *zap.Logger
}
func NewCommentHandler(service CommentService) *CommentHandler {
return &CommentHandler{
service: service,
logger: logger.Get(),
}
}
// CreateComment создает новый комментарий
func (h *CommentHandler) CreateComment(w http.ResponseWriter, r *http.Request) {
var req CreateCommentRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("Failed to decode request",
zap.Error(err),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
return
}
// Получаем userID из контекста (устанавливается AuthMiddleware)
userID, ok := r.Context().Value("user_id").(uint)
if !ok {
h.logger.Error("User ID not found in context")
util.ResponseWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
return
}
comment, err := h.service.CreateComment(userID, &req)
if err != nil {
h.logger.Error("Failed to create comment",
zap.Error(err),
zap.Uint("user_id", userID),
)
util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
util.ResponseWithJSON(w, http.StatusCreated, comment)
}
// GetCommentByID возвращает комментарий по ID
func (h *CommentHandler) GetCommentByID(w http.ResponseWriter, r *http.Request) {
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Error("Invalid comment ID",
zap.Error(err),
zap.String("id", idStr),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid comment ID"})
return
}
comment, err := h.service.GetCommentByID(uint(id))
if err != nil {
h.logger.Error("Failed to get comment",
zap.Error(err),
zap.Uint64("comment_id", id),
)
util.ResponseWithJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()})
return
}
util.ResponseWithJSON(w, http.StatusOK, comment)
}
// UpdateComment обновляет комментарий
func (h *CommentHandler) UpdateComment(w http.ResponseWriter, r *http.Request) {
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Error("Invalid comment ID",
zap.Error(err),
zap.String("id", idStr),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid comment ID"})
return
}
var req UpdateCommentRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("Failed to decode request",
zap.Error(err),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
return
}
userID, ok := r.Context().Value("user_id").(uint)
if !ok {
h.logger.Error("User ID not found in context")
util.ResponseWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
return
}
comment, err := h.service.UpdateComment(uint(id), userID, &req)
if err != nil {
h.logger.Error("Failed to update comment",
zap.Error(err),
zap.Uint64("comment_id", id),
zap.Uint("user_id", userID),
)
status := http.StatusInternalServerError
if err.Error() == "comment not found" {
status = http.StatusNotFound
} else if err.Error() == "you can only edit your own comments" {
status = http.StatusForbidden
}
util.ResponseWithJSON(w, status, map[string]string{"error": err.Error()})
return
}
util.ResponseWithJSON(w, http.StatusOK, comment)
}
// DeleteComment удаляет комментарий
func (h *CommentHandler) DeleteComment(w http.ResponseWriter, r *http.Request) {
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Error("Invalid comment ID",
zap.Error(err),
zap.String("id", idStr),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid comment ID"})
return
}
userID, ok := r.Context().Value("user_id").(uint)
if !ok {
h.logger.Error("User ID not found in context")
util.ResponseWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
return
}
// Проверяем, является ли пользователь администратором
isAdmin, _ := r.Context().Value("is_admin").(bool)
err = h.service.DeleteComment(uint(id), userID, isAdmin)
if err != nil {
h.logger.Error("Failed to delete comment",
zap.Error(err),
zap.Uint64("comment_id", id),
zap.Uint("user_id", userID),
zap.Bool("is_admin", isAdmin),
)
status := http.StatusInternalServerError
if err.Error() == "comment not found" {
status = http.StatusNotFound
} else if err.Error() == "you can only delete your own comments" {
status = http.StatusForbidden
}
util.ResponseWithJSON(w, status, map[string]string{"error": err.Error()})
return
}
util.ResponseWithJSON(w, http.StatusOK, map[string]string{"message": "Comment deleted successfully"})
}
// ListComments возвращает список комментариев с фильтрацией
func (h *CommentHandler) ListComments(w http.ResponseWriter, r *http.Request) {
var req ListCommentsRequest
if err := r.ParseForm(); err != nil {
h.logger.Error("Failed to parse form",
zap.Error(err),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid query parameters"})
return
}
// Парсим query параметры
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
req.Page = page
}
}
if sizeStr := r.URL.Query().Get("page_size"); sizeStr != "" {
if size, err := strconv.Atoi(sizeStr); err == nil && size > 0 && size <= 100 {
req.PageSize = size
}
}
if feedbackIDStr := r.URL.Query().Get("feedback_id"); feedbackIDStr != "" {
if id, err := strconv.ParseUint(feedbackIDStr, 10, 32); err == nil {
idUint := uint(id)
req.FeedbackID = &idUint
}
}
if authorIDStr := r.URL.Query().Get("author_id"); authorIDStr != "" {
if id, err := strconv.ParseUint(authorIDStr, 10, 32); err == nil {
idUint := uint(id)
req.AuthorID = &idUint
}
}
if parentIDStr := r.URL.Query().Get("parent_id"); parentIDStr != "" {
if id, err := strconv.ParseUint(parentIDStr, 10, 32); err == nil {
idUint := uint(id)
req.ParentID = &idUint
}
}
if verifiedStr := r.URL.Query().Get("verified"); verifiedStr != "" {
if verified, err := strconv.ParseBool(verifiedStr); err == nil {
req.Verified = &verified
}
}
if sortBy := r.URL.Query().Get("sort_by"); sortBy != "" {
req.SortBy = sortBy
}
if sortOrder := r.URL.Query().Get("sort_order"); sortOrder != "" {
req.SortOrder = sortOrder
}
comments, total, err := h.service.ListComments(&req)
if err != nil {
h.logger.Error("Failed to list comments",
zap.Error(err),
zap.Any("request", req),
)
util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
response := map[string]interface{}{
"data": comments,
"total": total,
"page": req.Page,
"page_size": req.PageSize,
"total_pages": (total + int64(req.PageSize) - 1) / int64(req.PageSize),
}
util.ResponseWithJSON(w, http.StatusOK, response)
}
// GetCommentsByFeedback возвращает комментарии по отзыву
func (h *CommentHandler) GetCommentsByFeedback(w http.ResponseWriter, r *http.Request) {
feedbackIDStr := chi.URLParam(r, "feedbackID")
feedbackID, err := strconv.ParseUint(feedbackIDStr, 10, 32)
if err != nil {
h.logger.Error("Invalid feedback ID",
zap.Error(err),
zap.String("feedback_id", feedbackIDStr),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid feedback ID"})
return
}
var req ListCommentsRequest
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
req.Page = page
}
}
if sizeStr := r.URL.Query().Get("page_size"); sizeStr != "" {
if size, err := strconv.Atoi(sizeStr); err == nil && size > 0 && size <= 100 {
req.PageSize = size
}
}
comments, total, err := h.service.GetCommentsByFeedback(uint(feedbackID), &req)
if err != nil {
h.logger.Error("Failed to get comments by feedback",
zap.Error(err),
zap.Uint64("feedback_id", feedbackID),
)
util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
response := map[string]interface{}{
"data": comments,
"total": total,
"page": req.Page,
"page_size": req.PageSize,
"total_pages": (total + int64(req.PageSize) - 1) / int64(req.PageSize),
}
util.ResponseWithJSON(w, http.StatusOK, response)
}
// GetMyComments возвращает комментарии текущего пользователя
func (h *CommentHandler) GetMyComments(w http.ResponseWriter, r *http.Request) {
userID, ok := r.Context().Value("user_id").(uint)
if !ok {
h.logger.Error("User ID not found in context")
util.ResponseWithJSON(w, http.StatusUnauthorized, map[string]string{"error": "User not authenticated"})
return
}
var req ListCommentsRequest
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
req.Page = page
}
}
if sizeStr := r.URL.Query().Get("page_size"); sizeStr != "" {
if size, err := strconv.Atoi(sizeStr); err == nil && size > 0 && size <= 100 {
req.PageSize = size
}
}
comments, total, err := h.service.GetCommentsByAuthor(userID, &req)
if err != nil {
h.logger.Error("Failed to get my comments",
zap.Error(err),
zap.Uint("user_id", userID),
)
util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
response := map[string]interface{}{
"data": comments,
"total": total,
"page": req.Page,
"page_size": req.PageSize,
"total_pages": (total + int64(req.PageSize) - 1) / int64(req.PageSize),
}
util.ResponseWithJSON(w, http.StatusOK, response)
}
// GetReplies возвращает ответы на комментарий
func (h *CommentHandler) GetReplies(w http.ResponseWriter, r *http.Request) {
parentIDStr := chi.URLParam(r, "parentID")
parentID, err := strconv.ParseUint(parentIDStr, 10, 32)
if err != nil {
h.logger.Error("Invalid parent comment ID",
zap.Error(err),
zap.String("parent_id", parentIDStr),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid parent comment ID"})
return
}
var req ListCommentsRequest
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
req.Page = page
}
}
if sizeStr := r.URL.Query().Get("page_size"); sizeStr != "" {
if size, err := strconv.Atoi(sizeStr); err == nil && size > 0 && size <= 100 {
req.PageSize = size
}
}
replies, total, err := h.service.GetReplies(uint(parentID), &req)
if err != nil {
h.logger.Error("Failed to get replies",
zap.Error(err),
zap.Uint64("parent_id", parentID),
)
util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
response := map[string]interface{}{
"data": replies,
"total": total,
"page": req.Page,
"page_size": req.PageSize,
"total_pages": (total + int64(req.PageSize) - 1) / int64(req.PageSize),
}
util.ResponseWithJSON(w, http.StatusOK, response)
}
// VerifyComment верифицирует комментарий (только для админов)
func (h *CommentHandler) VerifyComment(w http.ResponseWriter, r *http.Request) {
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
h.logger.Error("Invalid comment ID",
zap.Error(err),
zap.String("id", idStr),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid comment ID"})
return
}
var req struct {
Verified bool `json:"verified"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("Failed to decode request",
zap.Error(err),
)
util.ResponseWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
return
}
err = h.service.VerifyComment(uint(id), req.Verified)
if err != nil {
h.logger.Error("Failed to verify comment",
zap.Error(err),
zap.Uint64("comment_id", id),
zap.Bool("verified", req.Verified),
)
util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
status := "unverified"
if req.Verified {
status = "verified"
}
util.ResponseWithJSON(w, http.StatusOK, map[string]string{"message": "Comment " + status + " successfully"})
}
// GetCommentStats возвращает статистику по комментариям
func (h *CommentHandler) GetCommentStats(w http.ResponseWriter, r *http.Request) {
stats, err := h.service.GetCommentStats()
if err != nil {
h.logger.Error("Failed to get comment stats",
zap.Error(err),
)
util.ResponseWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
util.ResponseWithJSON(w, http.StatusOK, stats)
}