FCM
Описание#
Является сервисом-пройслойкой, который отвечает только за один тип уведомлений - отправку push-уведомлений на мобильные устройства пользователей.
Принцип работы#
Сервис принимает gRPC-запрос SendNotification от NotificationService и вызывает соответствующий обработчик.
Он, в свою очередь, вызывает обработчик SendNotification из UseCase слоя.
Модель данных в SendNotification#
type NotificationRequest struct {
ContactType int32 // тип контакта
User_UUID []string // уникальные идентификаторы пользователей
Message string // текст сообщения
Title string // заголовок
Target map[string]string // дополнительные параметры
}
SendNotification (UseCase) кладёт полученные данные в буферизированный канал.
При запуске сервиса запускается worker(), что он делает:
- Каждые 50мс проверяет очередь.
- Берет данные из канала и вызывает для них метод
sendNotification().
Что делает sendNotification():
- Вызывает метод из repo-слоя
GetTokensByUsers(), куда передает уникальные идентификаторы пользователей, тем самым получает их FCM-токены. - Проходится циклом по ответу БД и добавляет их в мапу, где ключом является UUID-пользователя, а значением - его FCM-токен.
- Проходится циклом по мапе, где на каждой итерации в отдельной горутине вызывает 'SaveNotification' из repo-слоя, сохраняя уведомление в БД.
- Далее в этом же цикле проходится ещё одним циклом по каждому FCM-токену и вызывает для него
checkAuth, проверяя токен на валидность черезCheckAuthService. Если токен оказался недействительным, то вызывает методDeleteTokenиз repo-слоя, удаляя тем самым его из БД. - Вызывает метод
SendPushдля попытки отправки уведомления. При любой ошибке - удаляет токен из базы данных. - В случае успеха возвращает
nil.
Что делает sendPush():
- Проверяет поле
target(доп. параметры) на их отсутствие, если их нет, то присваивает мапе значениеnil(в целях экономии памяти). -
Формирует структуру
&messaging.Message{}:message := &messaging.Message{ Notification: &messaging.Notification{ Title: title, // заголовок уведомления Body: body, // тело уведомления (содержание) }, Token: token, // FCM-токен пользователя Data: data, // дополнительные параметры (target) } -
Вызывает у клиента (фреймворка от Google - Firebase) метод
Send(), куда передаёт сформированное уведомление. В случае ошибки устанвливает статус в Span с кодом ошибки и её текстом, а также возвращает её. - В случае успеха возвращает
nil.
Также у сервиса есть ещё три ручки: FetchNotification, WatchNotification и SaveFCMToken
WatchNotification#
Этот метод помечает уведомления пользователя как прочитанные и сразу инвалидирует кеш списка уведомлений.
Модель данных#
type WatchNotificationRequest struct {
User_UUID string // уникальный идентификатор пользователя
Notification_UUID []string // уникальный идентификатор уведомления, если null, то прочитывает все существующие уведомления
}
- Вызывает метод
WatchNotificationизUseCase, в случае возвращения ошибки, логгирует её в Span и возвращает. - В
UseCaseвызываетсяWatchNotificationиз repo-слоя, в случае ошибки - логгируется в Span и возвращается. В случае успеха - кеш удаляется, возвращаетсяnil. - В repo-слое делается SQL-запрос, где у переданных уведомлений конкретного пользователя флаг
is_watchedменяет значение наtrue. Если передан пустой массивNotification_UUID- все уведомления пользователя отмечаются как прочитанные.
FetchNotification#
Этот метод позволяет получить список уведомлений.
Модель данных#
type FetchNotificationsRequest struct {
Filters FetchNotificationsRequest_Filters // фильтры
Pagination v2.PaginationRequest // параметры пагинации
}
type FetchNotificationsRequest_Filters struct {
User_UUID string // уникальный идентификатор пользователя
}
type PaginationRequest struct {
Limit *int32 // количество элементов на странице
Page *int32 // текущая страница
}
- Вызывает метод
FetchNotificationизUseCase, в случае ошибки - возвращает её. - В
UseCase:- Создаётся уникальный ключ кеша по
uuidпользователя. - Вызывается метод
GetOrSetCache, который либо получает данные из кеша, а в случае их отсутствия в кеше - из базы данных. В случае неудачи устанавливает статус в Span с кодом и текстом ошибки, а также возвращает её. - Выполняется пагинация. Высчитывается начало и конец страницы, вырезаются данные из списка уведомлений по индексам
startиend. - Формируется структура ответа
FetchNotificationResponse:&fcm.FetchNotificationsResponse{ Data: result.Data, // массив уведомлений Pagination: result.Pagination, // параметры пагинации UnwatchedCount: result.UnwatchedCount, // непрочитанные уведомления } - В случае успеха возвращается
nil.
- Создаётся уникальный ключ кеша по
SaveFCMToken#
Этот метод предназначен для сохранения FCM-токена пользователя в базу данных.
Модель данных#
type SaveFCMTokenRequest struct {
Token string // токен пользователя
User_UUID string // уникальный идентификатор пользователя
ApiKey string // ключ авторизации конкретного устройства
}
- Передаёт данные в
UseCaseслой. - В
UseCase- Формирует структуру для repo-слоя.
- Отправляет данные в repo-слой, где они сохраняются в БД
DeleteFCMTokens#
Этот метод предназначен для удаления FCM-токенов пользователей из базы данных.
Модель данных#
type DeleteFCMTokensRequest struct {
User_UUID string // уникальный идентификатор пользователя
}
- Вызывает метод
DeleteFCMTokensизUseCase, в случае ошибки - возвращает её. - В
UseCase:- Проверяет входные данные на пустоту, в случае ошибки - возвращает её.
- Вызывает
DeleteFCMTokensиз repo-слоя для удаления токенов. - В случае успеха - возвращает
nil.