On branch main
modified: main_dc/yalarba/api_yal/internal/domain/rating/dto.go new file: main_dc/yalarba/api_yal/internal/domain/rating/handler.go new file: main_dc/yalarba/api_yal/internal/domain/rating/router.go new file: main_dc/yalarba/api_yal/internal/domain/rating/service.go modified: main_dc/yalarba/api_yal/internal/router/router.go add raing domain without test
This commit is contained in:
@@ -1,6 +1,87 @@
|
||||
package rating
|
||||
|
||||
import (
|
||||
|
||||
"api_yal/internal/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CreateRatingRequest запрос на создание рейтинга
|
||||
type CreateRatingRequest struct {
|
||||
ObjectID uint `json:"object_id" validate:"required"`
|
||||
Platform models.PlatformType `json:"platform" validate:"required,oneof=entrepreneur tourist"`
|
||||
}
|
||||
|
||||
// UpdateRatingRequest запрос на обновление рейтинга
|
||||
type UpdateRatingRequest struct {
|
||||
AverageScore *float64 `json:"average_score,omitempty"`
|
||||
TotalVotes *int `json:"total_votes,omitempty"`
|
||||
}
|
||||
|
||||
// VoteRequest запрос на голосование
|
||||
type VoteRequest struct {
|
||||
Score int `json:"score" validate:"required,min=1,max=5"`
|
||||
}
|
||||
|
||||
// RatingResponse ответ с данными рейтинга
|
||||
type RatingResponse struct {
|
||||
ID uint `json:"id"`
|
||||
OwnerID uint `json:"owner_id"`
|
||||
ObjectID uint `json:"object_id"`
|
||||
Platform models.PlatformType `json:"platform"`
|
||||
AverageScore float64 `json:"average_score"`
|
||||
TotalVotes int `json:"total_votes"`
|
||||
VoteBreakdown *VoteBreakdownResponse `json:"vote_breakdown,omitempty"`
|
||||
Object *ObjectBriefResponse `json:"object,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// VoteBreakdownResponse ответ с детализацией голосов
|
||||
type VoteBreakdownResponse struct {
|
||||
Score1 int `json:"score_1"`
|
||||
Score2 int `json:"score_2"`
|
||||
Score3 int `json:"score_3"`
|
||||
Score4 int `json:"score_4"`
|
||||
Score5 int `json:"score_5"`
|
||||
}
|
||||
|
||||
// ObjectBriefResponse краткая информация об объекте
|
||||
type ObjectBriefResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// RatingVoteResponse ответ с данными голоса
|
||||
type RatingVoteResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Platform models.PlatformType `json:"platform"`
|
||||
TargetID uint `json:"target_id"`
|
||||
VoterID uint `json:"voter_id"`
|
||||
Score int `json:"score"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ListRatingsRequest параметры для списка рейтингов
|
||||
type ListRatingsRequest struct {
|
||||
Page int `query:"page" default:"1"`
|
||||
PageSize int `query:"page_size" default:"20"`
|
||||
Platform string `query:"platform"`
|
||||
OwnerID uint `query:"owner_id"`
|
||||
ObjectID uint `query:"object_id"`
|
||||
}
|
||||
|
||||
// ListRatingsResponse ответ со списком рейтингов
|
||||
type ListRatingsResponse struct {
|
||||
Ratings []RatingResponse `json:"ratings"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
// UserRatingInfoResponse информация о голосе пользователя
|
||||
type UserRatingInfoResponse struct {
|
||||
HasVoted bool `json:"has_voted"`
|
||||
UserScore *int `json:"user_score,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
package rating
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"api_yal/internal/logger"
|
||||
"api_yal/internal/middleware"
|
||||
"api_yal/internal/models"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type RatingHandler struct {
|
||||
service RatingService
|
||||
}
|
||||
|
||||
// NewRatingHandler создает новый обработчик рейтингов
|
||||
func NewRatingHandler(service RatingService) *RatingHandler {
|
||||
return &RatingHandler{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateRating создает новый рейтинг
|
||||
// @Summary Create a new rating
|
||||
// @Tags ratings
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body CreateRatingRequest true "Rating data"
|
||||
// @Success 201 {object} RatingResponse
|
||||
// @Router /ratings [post]
|
||||
func (h *RatingHandler) CreateRating(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := middleware.GetUserID(r.Context())
|
||||
if !ok {
|
||||
l.Debug("Unauthorized: user ID not found in context")
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
var req CreateRatingRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
l.Debug("Invalid request body", zap.Error(err))
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rating, err := h.service.CreateRating(r.Context(), userID, &req)
|
||||
if err != nil {
|
||||
l.Error("Failed to create rating", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(rating)
|
||||
}
|
||||
|
||||
// GetRatingByID возвращает рейтинг по ID
|
||||
// @Summary Get rating by ID
|
||||
// @Tags ratings
|
||||
// @Produce json
|
||||
// @Param id path int true "Rating ID"
|
||||
// @Success 200 {object} RatingResponse
|
||||
// @Router /ratings/{id} [get]
|
||||
func (h *RatingHandler) GetRatingByID(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid rating ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rating, err := h.service.GetRatingByID(r.Context(), uint(id))
|
||||
if err != nil {
|
||||
l.Error("Failed to get rating", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(rating)
|
||||
}
|
||||
|
||||
// GetRatingByObjectAndPlatform возвращает рейтинг объекта по платформе
|
||||
// @Summary Get rating by object ID and platform
|
||||
// @Tags ratings
|
||||
// @Produce json
|
||||
// @Param objectID path int true "Object ID"
|
||||
// @Param platform path string true "Platform (entrepreneur/tourist)"
|
||||
// @Success 200 {object} RatingResponse
|
||||
// @Router /ratings/object/{objectID}/platform/{platform} [get]
|
||||
func (h *RatingHandler) GetRatingByObjectAndPlatform(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
objectIDStr := chi.URLParam(r, "objectID")
|
||||
objectID, err := strconv.ParseUint(objectIDStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid object ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
platform := models.PlatformType(chi.URLParam(r, "platform"))
|
||||
if platform != models.PlatformEntrepreneur && platform != models.PlatformTourist {
|
||||
http.Error(w, "Invalid platform", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rating, err := h.service.GetRatingByObjectAndPlatform(r.Context(), uint(objectID), platform)
|
||||
if err != nil {
|
||||
l.Error("Failed to get rating", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(rating)
|
||||
}
|
||||
|
||||
// UpdateRating обновляет рейтинг
|
||||
// @Summary Update rating
|
||||
// @Tags ratings
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "Rating ID"
|
||||
// @Param request body UpdateRatingRequest true "Update data"
|
||||
// @Success 200 {object} RatingResponse
|
||||
// @Router /ratings/{id} [put]
|
||||
func (h *RatingHandler) UpdateRating(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid rating ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateRatingRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
l.Debug("Invalid request body", zap.Error(err))
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rating, err := h.service.UpdateRating(r.Context(), uint(id), &req)
|
||||
if err != nil {
|
||||
l.Error("Failed to update rating", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(rating)
|
||||
}
|
||||
|
||||
// DeleteRating удаляет рейтинг
|
||||
// @Summary Delete rating
|
||||
// @Tags ratings
|
||||
// @Param id path int true "Rating ID"
|
||||
// @Success 204 "No Content"
|
||||
// @Router /ratings/{id} [delete]
|
||||
func (h *RatingHandler) DeleteRating(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid rating ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.DeleteRating(r.Context(), uint(id)); err != nil {
|
||||
l.Error("Failed to delete rating", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// ListRatings возвращает список рейтингов
|
||||
// @Summary List ratings
|
||||
// @Tags ratings
|
||||
// @Produce json
|
||||
// @Param page query int false "Page number" default(1)
|
||||
// @Param page_size query int false "Page size" default(20)
|
||||
// @Param platform query string false "Filter by platform"
|
||||
// @Param owner_id query int false "Filter by owner ID"
|
||||
// @Param object_id query int false "Filter by object ID"
|
||||
// @Success 200 {object} ListRatingsResponse
|
||||
// @Router /ratings [get]
|
||||
func (h *RatingHandler) ListRatings(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
req := &ListRatingsRequest{}
|
||||
|
||||
// Парсинг query параметров
|
||||
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
|
||||
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
|
||||
req.Page = page
|
||||
}
|
||||
}
|
||||
if pageSizeStr := r.URL.Query().Get("page_size"); pageSizeStr != "" {
|
||||
if pageSize, err := strconv.Atoi(pageSizeStr); err == nil && pageSize > 0 {
|
||||
req.PageSize = pageSize
|
||||
}
|
||||
}
|
||||
req.Platform = r.URL.Query().Get("platform")
|
||||
if ownerIDStr := r.URL.Query().Get("owner_id"); ownerIDStr != "" {
|
||||
if ownerID, err := strconv.ParseUint(ownerIDStr, 10, 32); err == nil {
|
||||
req.OwnerID = uint(ownerID)
|
||||
}
|
||||
}
|
||||
if objectIDStr := r.URL.Query().Get("object_id"); objectIDStr != "" {
|
||||
if objectID, err := strconv.ParseUint(objectIDStr, 10, 32); err == nil {
|
||||
req.ObjectID = uint(objectID)
|
||||
}
|
||||
}
|
||||
|
||||
var response *ListRatingsResponse
|
||||
var err error
|
||||
|
||||
if req.ObjectID > 0 {
|
||||
// Получаем все рейтинги объекта
|
||||
ratings, err := h.service.GetRatingsByObject(r.Context(), req.ObjectID)
|
||||
if err != nil {
|
||||
l.Error("Failed to get ratings by object", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response = &ListRatingsResponse{
|
||||
Ratings: ratings,
|
||||
Total: int64(len(ratings)),
|
||||
Page: 1,
|
||||
PageSize: len(ratings),
|
||||
TotalPages: 1,
|
||||
}
|
||||
} else if req.OwnerID > 0 {
|
||||
response, err = h.service.GetRatingsByOwner(r.Context(), req.OwnerID, req)
|
||||
} else {
|
||||
response, err = h.service.ListRatings(r.Context(), req)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
l.Error("Failed to list ratings", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// Vote голосование за объект
|
||||
// @Summary Vote for an object
|
||||
// @Tags ratings
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body VoteRequest true "Vote data"
|
||||
// @Param targetID path int true "Target object ID"
|
||||
// @Param platform path string true "Platform"
|
||||
// @Success 200 {object} RatingResponse
|
||||
// @Router /ratings/{targetID}/vote/{platform} [post]
|
||||
func (h *RatingHandler) Vote(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := middleware.GetUserID(r.Context())
|
||||
if !ok {
|
||||
l.Debug("Unauthorized: user ID not found in context")
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
targetIDStr := chi.URLParam(r, "targetID")
|
||||
targetID, err := strconv.ParseUint(targetIDStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid target ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
platform := models.PlatformType(chi.URLParam(r, "platform"))
|
||||
if platform != models.PlatformEntrepreneur && platform != models.PlatformTourist {
|
||||
http.Error(w, "Invalid platform", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req VoteRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
l.Debug("Invalid request body", zap.Error(err))
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rating, err := h.service.Vote(r.Context(), userID, uint(targetID), platform, req.Score)
|
||||
if err != nil {
|
||||
l.Error("Failed to vote", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(rating)
|
||||
}
|
||||
|
||||
// GetMyVote возвращает голос текущего пользователя
|
||||
// @Summary Get current user's vote
|
||||
// @Tags ratings
|
||||
// @Produce json
|
||||
// @Param targetID path int true "Target object ID"
|
||||
// @Param platform path string true "Platform"
|
||||
// @Success 200 {object} UserRatingInfoResponse
|
||||
// @Router /ratings/{targetID}/my-vote/{platform} [get]
|
||||
func (h *RatingHandler) GetMyVote(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := middleware.GetUserID(r.Context())
|
||||
if !ok {
|
||||
l.Debug("Unauthorized: user ID not found in context")
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
targetIDStr := chi.URLParam(r, "targetID")
|
||||
targetID, err := strconv.ParseUint(targetIDStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid target ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
platform := models.PlatformType(chi.URLParam(r, "platform"))
|
||||
if platform != models.PlatformEntrepreneur && platform != models.PlatformTourist {
|
||||
http.Error(w, "Invalid platform", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
voteInfo, err := h.service.GetUserVote(r.Context(), userID, uint(targetID), platform)
|
||||
if err != nil {
|
||||
l.Error("Failed to get user vote", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(voteInfo)
|
||||
}
|
||||
|
||||
// UpdateMyVote обновляет голос текущего пользователя
|
||||
// @Summary Update current user's vote
|
||||
// @Tags ratings
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param targetID path int true "Target object ID"
|
||||
// @Param platform path string true "Platform"
|
||||
// @Param request body VoteRequest true "Vote data"
|
||||
// @Success 200 {object} RatingResponse
|
||||
// @Router /ratings/{targetID}/my-vote/{platform} [put]
|
||||
func (h *RatingHandler) UpdateMyVote(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := middleware.GetUserID(r.Context())
|
||||
if !ok {
|
||||
l.Debug("Unauthorized: user ID not found in context")
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
targetIDStr := chi.URLParam(r, "targetID")
|
||||
targetID, err := strconv.ParseUint(targetIDStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid target ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
platform := models.PlatformType(chi.URLParam(r, "platform"))
|
||||
if platform != models.PlatformEntrepreneur && platform != models.PlatformTourist {
|
||||
http.Error(w, "Invalid platform", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req VoteRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
l.Debug("Invalid request body", zap.Error(err))
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rating, err := h.service.UpdateUserVote(r.Context(), userID, uint(targetID), platform, req.Score)
|
||||
if err != nil {
|
||||
l.Error("Failed to update vote", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(rating)
|
||||
}
|
||||
|
||||
// DeleteMyVote удаляет голос текущего пользователя
|
||||
// @Summary Delete current user's vote
|
||||
// @Tags ratings
|
||||
// @Param targetID path int true "Target object ID"
|
||||
// @Param platform path string true "Platform"
|
||||
// @Success 204 "No Content"
|
||||
// @Router /ratings/{targetID}/my-vote/{platform} [delete]
|
||||
func (h *RatingHandler) DeleteMyVote(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := middleware.GetUserID(r.Context())
|
||||
if !ok {
|
||||
l.Debug("Unauthorized: user ID not found in context")
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
targetIDStr := chi.URLParam(r, "targetID")
|
||||
targetID, err := strconv.ParseUint(targetIDStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid target ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
platform := models.PlatformType(chi.URLParam(r, "platform"))
|
||||
if platform != models.PlatformEntrepreneur && platform != models.PlatformTourist {
|
||||
http.Error(w, "Invalid platform", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.DeleteUserVote(r.Context(), userID, uint(targetID), platform); err != nil {
|
||||
l.Error("Failed to delete vote", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// GetRatingStats возвращает статистику по рейтингам
|
||||
// @Summary Get rating statistics
|
||||
// @Tags ratings
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /ratings/stats [get]
|
||||
func (h *RatingHandler) GetRatingStats(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
stats, err := h.service.GetRatingStats(r.Context())
|
||||
if err != nil {
|
||||
l.Error("Failed to get rating stats", zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(stats)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package rating
|
||||
|
||||
import (
|
||||
"api_yal/internal/logger"
|
||||
"api_yal/internal/middleware"
|
||||
"api_yal/internal/repository"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RegisterRoutes регистрирует маршруты для рейтингов
|
||||
func RegisterRoutes(r chi.Router, db *gorm.DB, jwtSecret string) {
|
||||
l := logger.Get()
|
||||
l.Info("Registering routes for rating")
|
||||
|
||||
ratingRepo := repository.NewRatingRepository(db)
|
||||
ratingService := NewRatingServiceImpl(ratingRepo, db)
|
||||
ratingHandler := NewRatingHandler(ratingService)
|
||||
|
||||
// Группируем маршруты для рейтингов
|
||||
r.Route("/ratings", func(r chi.Router) {
|
||||
// Публичные маршруты (не требуют аутентификации)
|
||||
r.Get("/", ratingHandler.ListRatings)
|
||||
r.Get("/stats", ratingHandler.GetRatingStats)
|
||||
r.Get("/{id}", ratingHandler.GetRatingByID)
|
||||
r.Get("/object/{objectID}/platform/{platform}", ratingHandler.GetRatingByObjectAndPlatform)
|
||||
|
||||
// Защищенные маршруты (требуют аутентификации)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.AuthMiddleware(jwtSecret))
|
||||
|
||||
// CRUD операции с рейтингами
|
||||
r.Post("/", ratingHandler.CreateRating)
|
||||
r.Put("/{id}", ratingHandler.UpdateRating)
|
||||
r.Delete("/{id}", ratingHandler.DeleteRating)
|
||||
|
||||
// Голосование
|
||||
r.Post("/{targetID}/vote/{platform}", ratingHandler.Vote)
|
||||
r.Get("/{targetID}/my-vote/{platform}", ratingHandler.GetMyVote)
|
||||
r.Put("/{targetID}/my-vote/{platform}", ratingHandler.UpdateMyVote)
|
||||
r.Delete("/{targetID}/my-vote/{platform}", ratingHandler.DeleteMyVote)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
package rating
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"api_yal/internal/models"
|
||||
"api_yal/internal/repository"
|
||||
"api_yal/internal/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RatingService определяет интерфейс сервиса рейтингов
|
||||
type RatingService interface {
|
||||
// CreateRating создает новый рейтинг для объекта
|
||||
CreateRating(ctx context.Context, ownerID uint, req *CreateRatingRequest) (*RatingResponse, error)
|
||||
|
||||
// GetRatingByID возвращает рейтинг по ID
|
||||
GetRatingByID(ctx context.Context, id uint) (*RatingResponse, error)
|
||||
|
||||
// GetRatingByObjectAndPlatform возвращает рейтинг объекта по платформе
|
||||
GetRatingByObjectAndPlatform(ctx context.Context, objectID uint, platform models.PlatformType) (*RatingResponse, error)
|
||||
|
||||
// UpdateRating обновляет рейтинг
|
||||
UpdateRating(ctx context.Context, id uint, req *UpdateRatingRequest) (*RatingResponse, error)
|
||||
|
||||
// DeleteRating удаляет рейтинг
|
||||
DeleteRating(ctx context.Context, id uint) error
|
||||
|
||||
// ListRatings возвращает список рейтингов с пагинацией
|
||||
ListRatings(ctx context.Context, req *ListRatingsRequest) (*ListRatingsResponse, error)
|
||||
|
||||
// GetRatingsByObject возвращает все рейтинги объекта
|
||||
GetRatingsByObject(ctx context.Context, objectID uint) ([]RatingResponse, error)
|
||||
|
||||
// GetRatingsByOwner возвращает рейтинги владельца
|
||||
GetRatingsByOwner(ctx context.Context, ownerID uint, req *ListRatingsRequest) (*ListRatingsResponse, error)
|
||||
|
||||
// Vote голосование за объект
|
||||
Vote(ctx context.Context, voterID uint, targetID uint, platform models.PlatformType, score int) (*RatingResponse, error)
|
||||
|
||||
// GetUserVote возвращает голос пользователя
|
||||
GetUserVote(ctx context.Context, voterID uint, targetID uint, platform models.PlatformType) (*UserRatingInfoResponse, error)
|
||||
|
||||
// UpdateUserVote обновляет голос пользователя
|
||||
UpdateUserVote(ctx context.Context, voterID uint, targetID uint, platform models.PlatformType, newScore int) (*RatingResponse, error)
|
||||
|
||||
// DeleteUserVote удаляет голос пользователя
|
||||
DeleteUserVote(ctx context.Context, voterID uint, targetID uint, platform models.PlatformType) error
|
||||
|
||||
// GetRatingStats возвращает статистику по рейтингам
|
||||
GetRatingStats(ctx context.Context) (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
type ratingServiceImpl struct {
|
||||
ratingRepo repository.RatingRepository
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewRatingServiceImpl создает новый экземпляр сервиса рейтингов
|
||||
func NewRatingServiceImpl(ratingRepo repository.RatingRepository, db *gorm.DB) RatingService {
|
||||
return &ratingServiceImpl{
|
||||
ratingRepo: ratingRepo,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateRating создает новый рейтинг для объекта
|
||||
func (s *ratingServiceImpl) CreateRating(ctx context.Context, ownerID uint, req *CreateRatingRequest) (*RatingResponse, error) {
|
||||
l := logger.Get()
|
||||
|
||||
// Проверяем, существует ли уже рейтинг для этого объекта и платформы
|
||||
existing, _ := s.ratingRepo.GetByObjectAndPlatform(req.ObjectID, req.Platform)
|
||||
if existing != nil {
|
||||
return nil, errors.New("rating already exists for this object and platform")
|
||||
}
|
||||
|
||||
rating := &models.Rating{
|
||||
OwnerID: ownerID,
|
||||
ObjectID: req.ObjectID,
|
||||
Platform: req.Platform,
|
||||
AverageScore: 0,
|
||||
TotalVotes: 0,
|
||||
VoteBreakdown: models.VoteBreakdown{
|
||||
Score1: 0,
|
||||
Score2: 0,
|
||||
Score3: 0,
|
||||
Score4: 0,
|
||||
Score5: 0,
|
||||
},
|
||||
}
|
||||
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// Создаем рейтинг
|
||||
if err := s.ratingRepo.Create(rating); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Устанавливаем связь VoteBreakdown с рейтингом
|
||||
rating.VoteBreakdown.RatingID = rating.ID
|
||||
if err := s.ratingRepo.UpdateVoteBreakdown(&rating.VoteBreakdown); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Error("Failed to create rating", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.toRatingResponse(rating), nil
|
||||
}
|
||||
|
||||
// GetRatingByID возвращает рейтинг по ID
|
||||
func (s *ratingServiceImpl) GetRatingByID(ctx context.Context, id uint) (*RatingResponse, error) {
|
||||
rating, err := s.ratingRepo.GetByID(id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errors.New("rating not found")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return s.toRatingResponse(rating), nil
|
||||
}
|
||||
|
||||
// GetRatingByObjectAndPlatform возвращает рейтинг объекта по платформе
|
||||
func (s *ratingServiceImpl) GetRatingByObjectAndPlatform(ctx context.Context, objectID uint, platform models.PlatformType) (*RatingResponse, error) {
|
||||
rating, err := s.ratingRepo.GetByObjectAndPlatform(objectID, platform)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errors.New("rating not found")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return s.toRatingResponse(rating), nil
|
||||
}
|
||||
|
||||
// UpdateRating обновляет рейтинг
|
||||
func (s *ratingServiceImpl) UpdateRating(ctx context.Context, id uint, req *UpdateRatingRequest) (*RatingResponse, error) {
|
||||
rating, err := s.ratingRepo.GetByID(id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errors.New("rating not found")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.AverageScore != nil {
|
||||
rating.AverageScore = *req.AverageScore
|
||||
}
|
||||
if req.TotalVotes != nil {
|
||||
rating.TotalVotes = *req.TotalVotes
|
||||
}
|
||||
|
||||
if err := s.ratingRepo.Update(rating); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.toRatingResponse(rating), nil
|
||||
}
|
||||
|
||||
// DeleteRating удаляет рейтинг
|
||||
func (s *ratingServiceImpl) DeleteRating(ctx context.Context, id uint) error {
|
||||
// Проверяем существование
|
||||
if _, err := s.ratingRepo.GetByID(id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("rating not found")
|
||||
}
|
||||
return err
|
||||
}
|
||||
return s.ratingRepo.Delete(id)
|
||||
}
|
||||
|
||||
// ListRatings возвращает список рейтингов с пагинацией
|
||||
func (s *ratingServiceImpl) ListRatings(ctx context.Context, req *ListRatingsRequest) (*ListRatingsResponse, error) {
|
||||
if req.Page < 1 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize < 1 || req.PageSize > 100 {
|
||||
req.PageSize = 20
|
||||
}
|
||||
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
|
||||
var ratings []models.Rating
|
||||
var err error
|
||||
var total int64
|
||||
|
||||
// Фильтрация по платформе
|
||||
if req.Platform != "" {
|
||||
platform := models.PlatformType(req.Platform)
|
||||
ratings, err = s.ratingRepo.ListByPlatform(platform, offset, req.PageSize)
|
||||
if err == nil {
|
||||
total, _ = s.ratingRepo.Count()
|
||||
}
|
||||
} else if req.OwnerID > 0 {
|
||||
ratings, err = s.ratingRepo.ListByOwner(req.OwnerID, offset, req.PageSize)
|
||||
if err == nil {
|
||||
total, _ = s.ratingRepo.Count()
|
||||
}
|
||||
} else {
|
||||
ratings, err = s.ratingRepo.List(offset, req.PageSize)
|
||||
if err == nil {
|
||||
total, _ = s.ratingRepo.Count()
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responses := make([]RatingResponse, len(ratings))
|
||||
for i, rating := range ratings {
|
||||
responses[i] = *s.toRatingResponse(&rating)
|
||||
}
|
||||
|
||||
totalPages := int(total) / req.PageSize
|
||||
if int(total)%req.PageSize > 0 {
|
||||
totalPages++
|
||||
}
|
||||
|
||||
return &ListRatingsResponse{
|
||||
Ratings: responses,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetRatingsByObject возвращает все рейтинги объекта
|
||||
func (s *ratingServiceImpl) GetRatingsByObject(ctx context.Context, objectID uint) ([]RatingResponse, error) {
|
||||
ratings, err := s.ratingRepo.ListByObject(objectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responses := make([]RatingResponse, len(ratings))
|
||||
for i, rating := range ratings {
|
||||
responses[i] = *s.toRatingResponse(&rating)
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
// GetRatingsByOwner возвращает рейтинги владельца
|
||||
func (s *ratingServiceImpl) GetRatingsByOwner(ctx context.Context, ownerID uint, req *ListRatingsRequest) (*ListRatingsResponse, error) {
|
||||
if req.Page < 1 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize < 1 || req.PageSize > 100 {
|
||||
req.PageSize = 20
|
||||
}
|
||||
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
|
||||
ratings, err := s.ratingRepo.ListByOwner(ownerID, offset, req.PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
total, err := s.ratingRepo.Count()
|
||||
if err != nil {
|
||||
total = int64(len(ratings))
|
||||
}
|
||||
|
||||
responses := make([]RatingResponse, len(ratings))
|
||||
for i, rating := range ratings {
|
||||
responses[i] = *s.toRatingResponse(&rating)
|
||||
}
|
||||
|
||||
totalPages := int(total) / req.PageSize
|
||||
if int(total)%req.PageSize > 0 {
|
||||
totalPages++
|
||||
}
|
||||
|
||||
return &ListRatingsResponse{
|
||||
Ratings: responses,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Vote голосование за объект
|
||||
func (s *ratingServiceImpl) Vote(ctx context.Context, voterID uint, targetID uint, platform models.PlatformType, score int) (*RatingResponse, error) {
|
||||
l := logger.Get()
|
||||
|
||||
if score < 1 || score > 5 {
|
||||
return nil, errors.New("score must be between 1 and 5")
|
||||
}
|
||||
|
||||
var ratingResponse *RatingResponse
|
||||
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// Получаем или создаем рейтинг
|
||||
rating, err := s.ratingRepo.GetByObjectAndPlatform(targetID, platform)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// Создаем новый рейтинг
|
||||
rating = &models.Rating{
|
||||
OwnerID: 0, // Будет установлен из объекта
|
||||
ObjectID: targetID,
|
||||
Platform: platform,
|
||||
AverageScore: 0,
|
||||
TotalVotes: 0,
|
||||
VoteBreakdown: models.VoteBreakdown{
|
||||
Score1: 0,
|
||||
Score2: 0,
|
||||
Score3: 0,
|
||||
Score4: 0,
|
||||
Score5: 0,
|
||||
},
|
||||
}
|
||||
if err := s.ratingRepo.Create(rating); err != nil {
|
||||
return err
|
||||
}
|
||||
rating.VoteBreakdown.RatingID = rating.ID
|
||||
if err := s.ratingRepo.UpdateVoteBreakdown(&rating.VoteBreakdown); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем, не голосовал ли уже пользователь
|
||||
existingVote, _ := s.ratingRepo.GetRatingVote(targetID, voterID, platform)
|
||||
if existingVote != nil {
|
||||
return errors.New("user has already voted for this target")
|
||||
}
|
||||
|
||||
// Создаем голос
|
||||
vote := &models.RatingVote{
|
||||
Platform: platform,
|
||||
TargetID: targetID,
|
||||
VoterID: voterID,
|
||||
Score: score,
|
||||
}
|
||||
|
||||
if err := s.ratingRepo.CreateRatingVote(vote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Обновляем детализацию голосов
|
||||
breakdown := &rating.VoteBreakdown
|
||||
switch score {
|
||||
case 1:
|
||||
breakdown.Score1++
|
||||
case 2:
|
||||
breakdown.Score2++
|
||||
case 3:
|
||||
breakdown.Score3++
|
||||
case 4:
|
||||
breakdown.Score4++
|
||||
case 5:
|
||||
breakdown.Score5++
|
||||
}
|
||||
|
||||
rating.TotalVotes++
|
||||
rating.AverageScore = s.ratingRepo.CalculateAverageScore(breakdown)
|
||||
|
||||
if err := s.ratingRepo.UpdateVoteBreakdown(breakdown); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.ratingRepo.Update(rating); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ratingResponse = s.toRatingResponse(rating)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Error("Failed to process vote", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ratingResponse, nil
|
||||
}
|
||||
|
||||
// GetUserVote возвращает голос пользователя
|
||||
func (s *ratingServiceImpl) GetUserVote(ctx context.Context, voterID uint, targetID uint, platform models.PlatformType) (*UserRatingInfoResponse, error) {
|
||||
vote, err := s.ratingRepo.GetRatingVote(targetID, voterID, platform)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return &UserRatingInfoResponse{HasVoted: false}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &UserRatingInfoResponse{
|
||||
HasVoted: true,
|
||||
UserScore: &vote.Score,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateUserVote обновляет голос пользователя
|
||||
func (s *ratingServiceImpl) UpdateUserVote(ctx context.Context, voterID uint, targetID uint, platform models.PlatformType, newScore int) (*RatingResponse, error) {
|
||||
l := logger.Get()
|
||||
|
||||
if newScore < 1 || newScore > 5 {
|
||||
return nil, errors.New("score must be between 1 and 5")
|
||||
}
|
||||
|
||||
var ratingResponse *RatingResponse
|
||||
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// Получаем существующий голос
|
||||
vote, err := s.ratingRepo.GetRatingVote(targetID, voterID, platform)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("vote not found")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Получаем рейтинг
|
||||
rating, err := s.ratingRepo.GetByObjectAndPlatform(targetID, platform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldScore := vote.Score
|
||||
|
||||
// Обновляем детализацию голосов
|
||||
breakdown := &rating.VoteBreakdown
|
||||
|
||||
// Удаляем старую оценку
|
||||
switch oldScore {
|
||||
case 1:
|
||||
breakdown.Score1--
|
||||
case 2:
|
||||
breakdown.Score2--
|
||||
case 3:
|
||||
breakdown.Score3--
|
||||
case 4:
|
||||
breakdown.Score4--
|
||||
case 5:
|
||||
breakdown.Score5--
|
||||
}
|
||||
|
||||
// Добавляем новую оценку
|
||||
switch newScore {
|
||||
case 1:
|
||||
breakdown.Score1++
|
||||
case 2:
|
||||
breakdown.Score2++
|
||||
case 3:
|
||||
breakdown.Score3++
|
||||
case 4:
|
||||
breakdown.Score4++
|
||||
case 5:
|
||||
breakdown.Score5++
|
||||
}
|
||||
|
||||
// Обновляем голос
|
||||
vote.Score = newScore
|
||||
if err := s.ratingRepo.UpdateRatingVote(vote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Обновляем рейтинг
|
||||
rating.AverageScore = s.ratingRepo.CalculateAverageScore(breakdown)
|
||||
if err := s.ratingRepo.UpdateVoteBreakdown(breakdown); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ratingRepo.Update(rating); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ratingResponse = s.toRatingResponse(rating)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Error("Failed to update vote", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ratingResponse, nil
|
||||
}
|
||||
|
||||
// DeleteUserVote удаляет голос пользователя
|
||||
func (s *ratingServiceImpl) DeleteUserVote(ctx context.Context, voterID uint, targetID uint, platform models.PlatformType) error {
|
||||
l := logger.Get()
|
||||
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
// Получаем существующий голос
|
||||
vote, err := s.ratingRepo.GetRatingVote(targetID, voterID, platform)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("vote not found")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Получаем рейтинг
|
||||
rating, err := s.ratingRepo.GetByObjectAndPlatform(targetID, platform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Обновляем детализацию голосов
|
||||
breakdown := &rating.VoteBreakdown
|
||||
switch vote.Score {
|
||||
case 1:
|
||||
breakdown.Score1--
|
||||
case 2:
|
||||
breakdown.Score2--
|
||||
case 3:
|
||||
breakdown.Score3--
|
||||
case 4:
|
||||
breakdown.Score4--
|
||||
case 5:
|
||||
breakdown.Score5--
|
||||
}
|
||||
|
||||
rating.TotalVotes--
|
||||
rating.AverageScore = s.ratingRepo.CalculateAverageScore(breakdown)
|
||||
|
||||
// Удаляем голос
|
||||
if err := s.ratingRepo.DeleteRatingVote(vote.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Обновляем рейтинг
|
||||
if err := s.ratingRepo.UpdateVoteBreakdown(breakdown); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ratingRepo.Update(rating); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Error("Failed to delete vote", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRatingStats возвращает статистику по рейтингам
|
||||
func (s *ratingServiceImpl) GetRatingStats(ctx context.Context) (map[string]interface{}, error) {
|
||||
totalCount, err := s.ratingRepo.Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats := map[string]interface{}{
|
||||
"total_ratings": totalCount,
|
||||
"timestamp": time.Now(),
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// toRatingResponse конвертирует модель Rating в RatingResponse
|
||||
func (s *ratingServiceImpl) toRatingResponse(rating *models.Rating) *RatingResponse {
|
||||
resp := &RatingResponse{
|
||||
ID: rating.ID,
|
||||
OwnerID: rating.OwnerID,
|
||||
ObjectID: rating.ObjectID,
|
||||
Platform: rating.Platform,
|
||||
AverageScore: rating.AverageScore,
|
||||
TotalVotes: rating.TotalVotes,
|
||||
CreatedAt: rating.CreatedAt,
|
||||
UpdatedAt: rating.UpdatedAt,
|
||||
}
|
||||
|
||||
if rating.VoteBreakdown.ID != 0 {
|
||||
resp.VoteBreakdown = &VoteBreakdownResponse{
|
||||
Score1: rating.VoteBreakdown.Score1,
|
||||
Score2: rating.VoteBreakdown.Score2,
|
||||
Score3: rating.VoteBreakdown.Score3,
|
||||
Score4: rating.VoteBreakdown.Score4,
|
||||
Score5: rating.VoteBreakdown.Score5,
|
||||
}
|
||||
}
|
||||
|
||||
if rating.Object.ID != 0 {
|
||||
resp.Object = &ObjectBriefResponse{
|
||||
ID: rating.Object.ID,
|
||||
Name: fmt.Sprintf("Object %d", rating.Object.ID), // В реальности нужно брать из Object.Name
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"api_yal/internal/domain/comment"
|
||||
"api_yal/internal/domain/feetback"
|
||||
"api_yal/internal/domain/object"
|
||||
"api_yal/internal/domain/rating"
|
||||
"api_yal/internal/logger"
|
||||
"time"
|
||||
|
||||
@@ -57,14 +58,19 @@ func SetupRouter(db *gorm.DB, config *config.Config) http.Handler {
|
||||
// Регистрируем маршруты аккаунтов
|
||||
account.RegisterRoutes(r, db, config.JWTSecret)
|
||||
|
||||
// Регистрируем маршруты объектов
|
||||
// Регистрируем маршурты обьектов
|
||||
object.RegisterRoutes(r, db, config.JWTSecret)
|
||||
|
||||
// Регистрируем маршруты отзывов
|
||||
feetback.RegisterRoutes(r, db, config.JWTSecret)
|
||||
|
||||
// Регистрируем маршруты комментариев
|
||||
// Регистрация маршрутов для комментариев
|
||||
comment.RegisterRoutes(r, db, config.JWTSecret)
|
||||
|
||||
// Регистрация маршрутов для райтинга
|
||||
rating.RegisterRoutes(r, db, config.JWTSecret)
|
||||
|
||||
|
||||
})
|
||||
|
||||
zapLogger.Info("Настройка маршрутов завершена")
|
||||
|
||||
Reference in New Issue
Block a user