2025-10-31 17:54:29 +07:00

253 lines
8.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"encoding/json"
"net/http"
"strconv"
"time"
"effective_mobile_test_go_api/internal/model"
"effective_mobile_test_go_api/internal/service"
"github.com/google/uuid"
"github.com/gorilla/mux"
"go.uber.org/zap"
)
type Handler struct {
log *zap.Logger
service *service.SubscriptionService
}
func NewHandler(log *zap.Logger, service *service.SubscriptionService) *Handler {
return &Handler{log: log, service: service}
}
type CreateSubscriptionRequest struct {
ServiceName string `json:"service_name" example:"Yandex Plus"`
Price int `json:"price" example:"400"`
UserID string `json:"user_id" example:"60601fee-2bf1-4721-ae6f-7636e79a0cba"`
StartDate string `json:"start_date" example:"07-2025"`
EndDate *string `json:"end_date,omitempty" example:"12-2025"`
}
// CreateSubscription godoc
// @Summary Создать подписку
// @Tags subscriptions
// @Accept json
// @Produce json
// @Param subscription body CreateSubscriptionRequest true "Создание подписки"
// @Success 201 {object} model.Subscription
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /subscriptions [post]
func (h *Handler) CreateSubscription(w http.ResponseWriter, r *http.Request) {
var req CreateSubscriptionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.log.Error("Ошибка декодирования", zap.Error(err))
http.Error(w, "Ошибка запроса", http.StatusBadRequest)
return
}
userUUID, err := uuid.Parse(req.UserID)
if err != nil {
http.Error(w, "Неверный uuid пользователя", http.StatusBadRequest)
return
}
startDate, err := time.Parse("01-2006", req.StartDate)
if err != nil {
http.Error(w, "Неверный формат start_date", http.StatusBadRequest)
return
}
var endDate *time.Time
if req.EndDate != nil {
ed, err := time.Parse("01-2006", *req.EndDate)
if err != nil {
http.Error(w, "Неверный формат даты окончания", http.StatusBadRequest)
return
}
endDate = &ed
}
sub := &model.Subscription{
ServiceName: req.ServiceName,
Price: req.Price,
UserID: userUUID,
StartDate: startDate,
EndDate: endDate,
}
if err := h.service.Create(r.Context(), sub); err != nil {
h.log.Error("Подписчик не создан", zap.Error(err))
http.Error(w, "Ошибка создания", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(sub)
}
// GetSubscription godoc
// @Summary Получить подписку по ID
// @Tags subscriptions
// @Produce json
// @Param id path int true "ID подписки"
// @Success 200 {object} model.Subscription
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /subscriptions/{id} [get]
func (h *Handler) GetSubscription(w http.ResponseWriter, r *http.Request) {
idStr := mux.Vars(r)["id"]
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Неверный идентификатор", http.StatusBadRequest)
return
}
sub, err := h.service.GetByID(r.Context(), id)
if err != nil {
http.Error(w, "Подписчик не найден", http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(sub)
}
// UpdateSubscription godoc
// @Summary Обновить подписку по ID
// @Tags subscriptions
// @Accept json
// @Produce json
// @Param id path int true "ID подписки"
// @Param subscription body CreateSubscriptionRequest true "Обновление подписки"
// @Success 200 {object} model.Subscription
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /subscriptions/{id} [put]
func (h *Handler) UpdateSubscription(w http.ResponseWriter, r *http.Request) {
idStr := mux.Vars(r)["id"]
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Неверный идентификатор", http.StatusBadRequest)
return
}
var req CreateSubscriptionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Неверный запрос", http.StatusBadRequest)
return
}
userUUID, err := uuid.Parse(req.UserID)
if err != nil {
http.Error(w, "Неверный uuid пользователя", http.StatusBadRequest)
return
}
startDate, err := time.Parse("01-2006", req.StartDate)
if err != nil {
http.Error(w, "Неверный формат даты начала", http.StatusBadRequest)
return
}
var endDate *time.Time
if req.EndDate != nil {
ed, err := time.Parse("01-2006", *req.EndDate)
if err != nil {
http.Error(w, "Неверный формат даты окончания", http.StatusBadRequest)
return
}
endDate = &ed
}
sub := &model.Subscription{
ID: id,
ServiceName: req.ServiceName,
Price: req.Price,
UserID: userUUID,
StartDate: startDate,
EndDate: endDate,
}
if err := h.service.Update(r.Context(), sub); err != nil {
http.Error(w, "Не удалось обновить", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(sub)
}
// DeleteSubscription godoc
// @Summary Удалить подписку по ID
// @Tags subscriptions
// @Param id path int true "ID подписки"
// @Success 204 "No Content"
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /subscriptions/{id} [delete]
func (h *Handler) DeleteSubscription(w http.ResponseWriter, r *http.Request) {
idStr := mux.Vars(r)["id"]
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Неверный идентификатор", http.StatusBadRequest)
return
}
if err := h.service.Delete(r.Context(), id); err != nil {
http.Error(w, "Не удалось удалить", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}
// ListSubscriptions godoc
// @Summary Список подписок
// @Tags subscriptions
// @Produce json
// @Success 200 {array} model.Subscription
// @Failure 500 {object} map[string]string
// @Router /subscriptions [get]
func (h *Handler) ListSubscriptions(w http.ResponseWriter, r *http.Request) {
subs, err := h.service.List(r.Context())
if err != nil {
http.Error(w, "Не удалось включить в список", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(subs)
}
// SumSubscriptions godoc
// @Summary Суммарная стоимость подписок за период с фильтрацией
// @Tags subscriptions
// @Produce json
// @Param user_id query string false "UUID пользователя"
// @Param service_name query string false "Название сервиса"
// @Param start query string true "Начало периода, формат MM-YYYY" example("07-2025")
// @Param end query string true "Конец периода, формат MM-YYYY" example("08-2025")
// @Success 200 {object} map[string]int
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /subscriptions/sum [get]
func (h *Handler) SumSubscriptions(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
var userID *uuid.UUID
if u := q.Get("user_id"); u != "" {
id, err := uuid.Parse(u)
if err != nil {
http.Error(w, "Неверный идентификатор пользователя", http.StatusBadRequest)
return
}
userID = &id
}
serviceName := q.Get("service_name")
startStr := q.Get("start")
endStr := q.Get("end")
if startStr == "" || endStr == "" {
http.Error(w, "требуются начальные и конечные параметры запроса", http.StatusBadRequest)
return
}
startDate, err := time.Parse("01-2006", startStr)
if err != nil {
http.Error(w, "Неверная дата начала", http.StatusBadRequest)
return
}
endDate, err := time.Parse("01-2006", endStr)
if err != nil {
http.Error(w, "Неверная дата окончания", http.StatusBadRequest)
return
}
sum, err := h.service.SumPrice(r.Context(), userID, serviceName, startDate, endDate)
if err != nil {
http.Error(w, "Не удалось рассчитать сумму", http.StatusInternalServerError)
return
}
resp := map[string]int{"total_price": sum}
json.NewEncoder(w).Encode(resp)
}