package controller import ( "github.com/gofiber/fiber/v2" "go.uber.org/zap" "gitea.pena/PenaSide/feedback/internal/client" "gitea.pena/PenaSide/feedback/internal/models" "gitea.pena/PenaSide/feedback/internal/repository" "time" ) // FeedbackController - контроллер формы обратной связи type FeedbackController struct { logger *zap.Logger repository *repository.FeedbackRepository telegram *client.Telegram queue *FeedbackQueue interrupter chan bool // Канал для прерывания работы сервиса контроллера } // NewFeedbackController - создать контроллер формы обратной связи func NewFeedbackController( logger *zap.Logger, rep *repository.FeedbackRepository, tg *client.Telegram) *FeedbackController { return &FeedbackController{logger: logger, repository: rep, telegram: tg, queue: NewFeedbackQueue(), interrupter: make(chan bool, 1)} } // Register - регистрирует путь в fiber. // // Method: POST // Path: /callme // Name: callMe func (r *FeedbackController) Register() (method, path, name string, handler fiber.Handler) { return "POST", "/callme", "callMe", r.Handler } // Handler - Метод для отправки фидбэка. Складывает запрос в boltdb и отправляет на обработку в очередь // // Request: models.ReqFeedback // // Responses: // Success // Status: 200 // Body: nil // // Bad request - parsing error // Status: 400 // Body: error // // Bad request - validation error // Status: 400 // Body: { {field: string, tag: string, value: string}, ... } // // Internal server error - repository insert error // Status: 500 // Body: error // // Service Unavailable - enqueue error // Status: 503 // Body: nil func (r *FeedbackController) Handler(c *fiber.Ctx) error { var req models.ReqFeedback err := c.BodyParser(&req) if err != nil { return fiber.NewError(fiber.StatusBadRequest, err.Error()) } errValidate := validateStruct(&req) if errValidate != nil { return c.Status(fiber.StatusBadRequest).JSON(errValidate) } feedback := models.NewFeedback(c.Hostname(), req.Contact, req.WhoAmi) // складываем в BoltDB на безопасное хранение err = r.repository.Insert(feedback) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } // отправляем в очередь сервиса if !r.queue.Enqueue(feedback) { return fiber.NewError(fiber.StatusServiceUnavailable) } return c.SendStatus(fiber.StatusOK) } // WarmUpService - прогревает сервис контроллера формы обратной связи перед запуском. Отправляет все фибдбэки из репозитория клиенту func (r *FeedbackController) WarmUpService() error { // достаем из BoltDB записи tasks, err := r.repository.GetAll() if err != nil { return err } for _, task := range tasks { _ = r.do(task) } return nil } // RunService - запуск сервиса контроллера формы обратной связи func (r *FeedbackController) RunService() { // запускаем цикл проверки очереди for { if r.queue.Len() > 0 { task := r.queue.Dequeue() if err := r.do(task); err != nil { continue } } select { case <-r.interrupter: break default: time.Sleep(time.Second) } } } // do - отправляет фидбэк в клиент и удаляет его из репозитория func (r *FeedbackController) do(task *models.Feedback) error { if task != nil { if err := r.telegram.SendFeedback(task); err != nil { r.logger.Error("CanNotSendFeedback", zap.Error(err)) return err } if err := r.repository.Delete(task.GetID()); err != nil { r.logger.Error("CanNotDeleteFeedback", zap.Error(err)) return err } } return nil } // StopService - остановить сервис контроллера формы обратной связи func (r *FeedbackController) StopService() { r.interrupter <- true }