253 lines
8.9 KiB
Go
253 lines
8.9 KiB
Go
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)
|
||
}
|