cc3d0a8b07
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
438 lines
13 KiB
Go
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)
|
|
} |