Skip to content

Email

Описание#

Является сервисом-пройслойкой, который отвечает только за один тип уведомлений - отправку сообщений по электронной почте.

Принцип работы#

Сервис принимает gRPC-запрос SendNotification от NotificationService.

Модель входных данных в SendNotification#
    message NotificationRequest {
        string title              = 1;  // тема сообщения
        string category_message   = 2;  // категория сообщени (required)
        int32  contact_type       = 3;  // тип контакта (required)
        StringValue user_UUID     = 4;  // уникальный идентификатор пользователя (optional)
        StringValue message       = 5;  // текст сообщения (optional)
        repeated string contacts  = 6;  // email-адреса
        Int32Value secret_code    = 7;  // код подтверждения
    }
Категории сообщений#
    enum MessageCategories {
        NOT_USED = 0; // не используется
        VERIFY_CONTACT = 1; // подтверждение контакта
        SOME_INFO = 2; // информационное письмо
        RESET_PASSWORD = 3; // сброс пароля
        SIGN_IN = 4; // вход/авторизация
        CREATE_REGISTRATION = 5; // уведомление о регистрации на мероприятие
        SIGN_UP_VERIFY = 6; // подтверждениие регистрации аккаунта
    }

Обработчик проверяет категорию и тему сообщения, в случае если категория SOME_INFO и заголовок пуст, возвращает status.Error() с соответствующим кодом и текстом. Далее вызывает метод SendNotification из usecase слоя. Этот метод кладёт данные в буферизированный канал.

На один инстанс сервиса запущен один worker(). Что он делает:

  • Каждые 250мс проверяет очередь.
  • Берёт запрос из канала и вызывает для него send().

Что делает send():

  • В Switch'ится по категории сообщения, если она: VERIFY_CONTACT, RESET_PASSWORD, SIGN_IN, SIGN_UP_VERIFY, то:
    • Устанавливает ru локаль.
    • Вызывает метод confirmationCodeTemplateFunc(), где происходит подгонка сообщения под шаблон.
    • Полю Message присваивается значение переменной, которая вернулась из confirmationCodeTemplateFunc().
    • В случае неудачи - возвращается ошибка.
  • Если же категория сообщения CREATE_REGISTRATION, то:
    • Проверяется по Message на nil, если оно nil, то ошибка логгируется в Sentry.
    • Вызывает метод createRegistration(), где происходит подгонка сообщения под соответствующий шаблон.
    • В случае неудачи - возвращается ошибка.
  • Далее создаётся структура типа &mail.Mail{}, где
        emailMessage := &mail.Email{
            To:      req.Contacts, // массив email-адресов получателей
            From:    uc.cfg.SMTP.Sender, // адрес отправителя
            Subject: req.Title, // заголовок письма
            HTML:    []byte(*req.Message), // готовый HTML-текст письма
            Headers: textproto.MIMEHeader{}, // доп. заголовки (пустые, не используются)
        }
    
  • У emailMessage вызывается метод Send(), куда передаётся адрес SMTP-сервера и объект аутентификации. В случае ошибки логгирует её в Span, создаёт структуру Emaillog из repo-слоя, куда записывает данные о контексте ошибки, вызывает метод SaveLog для сохранения этих данных в БД, возвращает ошибку.
  • В случае успеха, создаёт такую же структуру Emaillog, но со значением поля ErrorText - nil, также передаёт её в метод SaveLog и в качестве ошибки возвращает nil.