forked from CITKParus/P8-ExchangeService
Модуль обработки очереди уведомлений о работе сервера приложений
This commit is contained in:
parent
af5f93380f
commit
9dbf65237d
181
core/notifier.js
Normal file
181
core/notifier.js
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
Сервис интеграции ПП Парус 8 с WEB API
|
||||
Модуль ядра: модуль рассылки уведомлений
|
||||
*/
|
||||
|
||||
//------------------------------
|
||||
// Подключение внешних библиотек
|
||||
//------------------------------
|
||||
|
||||
const _ = require("lodash"); //Работа с массивами и коллекциями
|
||||
const EventEmitter = require("events"); //Обработчик пользовательских событий
|
||||
const { ServerError } = require("./server_errors"); //Типовая ошибка
|
||||
const { SERR_OBJECT_BAD_INTERFACE } = require("./constants"); //Общесистемные константы
|
||||
const { makeErrorText, validateObject, sendMail } = require("./utils"); //Вспомогательные функции
|
||||
const prmsNotifierSchema = require("../models/prms_notifier"); //Схемы валидации параметров функций класса
|
||||
|
||||
//--------------------------
|
||||
// Глобальные идентификаторы
|
||||
//--------------------------
|
||||
|
||||
//Типовые события
|
||||
const SEVT_NOTIFIER_STARTED = "NOTIFIER_STARTED"; //Модуль рассылки уведомлений запущен
|
||||
const SEVT_NOTIFIER_STOPPED = "NOTIFIER_STOPPED"; //Модуль рассылки уведомлений остановлен
|
||||
|
||||
//Время отложенного старта цикла отправки сообщений (мс)
|
||||
const NSEND_LOOP_DELAY = 3000;
|
||||
|
||||
//Интервал проверки очереди отправки сообщений (мс)
|
||||
const NSEND_LOOP_INTERVAL = 3000;
|
||||
|
||||
//------------
|
||||
// Тело модуля
|
||||
//------------
|
||||
|
||||
//Класс рассылки уведомлений
|
||||
class Notifier extends EventEmitter {
|
||||
//Конструктор класса
|
||||
constructor(prms) {
|
||||
//Создадим экземпляр родительского класса
|
||||
super();
|
||||
//Проверяем структуру переданного набора параметров для конструктора
|
||||
let sCheckResult = validateObject(prms, prmsNotifierSchema.Notifier, "Параметры конструктора класса Notifier");
|
||||
//Если структура объекта в норме
|
||||
if (!sCheckResult) {
|
||||
//Очередь отправляемых уведомлений
|
||||
this.messages = [];
|
||||
//Признак функционирования модуля
|
||||
this.bWorking = false;
|
||||
//Признак необходимости оповещения об останове
|
||||
this.bNotifyStopped = false;
|
||||
//Флаг работы цикла проверки
|
||||
this.bInSendLoop = false;
|
||||
//Идентификатор таймера проверки очереди отправки уведомлений
|
||||
this.nSendLoopTimeOut = null;
|
||||
//Запомним параметры отправки E-Mail
|
||||
this.mail = prms.mail;
|
||||
//Запомним логгер
|
||||
this.logger = prms.logger;
|
||||
//Привяжем методы к указателю на себя для использования в обработчиках событий
|
||||
this.notifySendLoop = this.notifySendLoop.bind(this);
|
||||
} else {
|
||||
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
|
||||
}
|
||||
}
|
||||
//Добавление уведомления в очередь отправки
|
||||
addMessage(prms) {
|
||||
//Проверяем структуру переданного объекта для старта
|
||||
let sCheckResult = validateObject(
|
||||
prms,
|
||||
prmsNotifierSchema.addMessage,
|
||||
"Параметры функции добавления уведомления в очередь отправки"
|
||||
);
|
||||
//Если структура объекта в норме
|
||||
if (!sCheckResult) {
|
||||
let tmp = _.cloneDeep(prms);
|
||||
tmp.bSent = false;
|
||||
this.messages.push(tmp);
|
||||
} else {
|
||||
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
|
||||
}
|
||||
}
|
||||
//Уведомление о запуске модуля
|
||||
notifyStarted() {
|
||||
//Оповестим подписчиков о запуске
|
||||
this.emit(SEVT_NOTIFIER_STARTED);
|
||||
}
|
||||
//Уведомление об остановке модуля
|
||||
notifyStopped() {
|
||||
//Оповестим подписчиков об останове
|
||||
this.emit(SEVT_NOTIFIER_STOPPED);
|
||||
}
|
||||
//Перезапуск отправки очереди уведомлений
|
||||
restartSendLoop() {
|
||||
//Включаем опрос очереди уведомлений только если установлен флаг работы
|
||||
if (this.bWorking) {
|
||||
this.nSendLoopTimeOut = setTimeout(this.notifySendLoop, NSEND_LOOP_INTERVAL);
|
||||
} else {
|
||||
//Если мы не работаем и просили оповестить об останове (видимо была команда на останов) - сделаем это
|
||||
if (this.bNotifyStopped) this.notifyStopped();
|
||||
}
|
||||
}
|
||||
//Отправка уведомлений из очереди
|
||||
async notifySendLoop() {
|
||||
//Выставим флаг - цикл опроса активен
|
||||
this.bInSendLoop = true;
|
||||
//Обходим уведомления для отправки
|
||||
for (let i = 0; i < this.messages.length; i++) {
|
||||
//Работаем только по неотправленным уведомлениям
|
||||
if (!this.messages[i].bSent) {
|
||||
try {
|
||||
//Отправляем
|
||||
await sendMail({
|
||||
mail: this.mail,
|
||||
sTo: this.messages[i].sTo,
|
||||
sSubject: this.messages[i].sSubject,
|
||||
sMessage: this.messages[i].sMessage
|
||||
});
|
||||
//Протоколируем отправку
|
||||
await this.logger.info(
|
||||
`Сообщение с темой "${this.messages[i].sSubject}" отпрвлено ${this.messages[i].sTo}`
|
||||
);
|
||||
//Говорим, что отправлено
|
||||
this.messages[i].bSent = true;
|
||||
} catch (e) {
|
||||
await this.logger.error(
|
||||
`Ошибка отправки сообщения с темой "${this.messages[i].sSubject}" для ${
|
||||
this.messages[i].sTo
|
||||
}: ${makeErrorText(e)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Подчищаем очередь - удалим уже отправленные
|
||||
_.remove(this.messages, { bSent: true });
|
||||
//Выставим флаг - цикл опроса неактивен
|
||||
this.bInSendLoop = false;
|
||||
//Перезапускаем опрос
|
||||
this.restartSendLoop();
|
||||
}
|
||||
//Запуск модуля
|
||||
startNotifier() {
|
||||
//Выставляем флаг работы
|
||||
this.bWorking = true;
|
||||
//Выставляем флаг необходимости оповещения об останове
|
||||
this.bNotifyStopped = false;
|
||||
//Выставляем флаг неактивности (пока) цикла опроса
|
||||
this.bInSendLoop = false;
|
||||
//Начинаем слушать очередь исходящих
|
||||
setTimeout(this.notifySendLoop, NSEND_LOOP_DELAY);
|
||||
//И оповещаем всех что запустились
|
||||
this.notifyStarted();
|
||||
}
|
||||
//Остановка модуля
|
||||
stopNotifier() {
|
||||
//Выставляем флаг неработы
|
||||
this.bWorking = false;
|
||||
//Если сейчас мы не в цикле проверки
|
||||
if (!this.bInSendLoop) {
|
||||
//Сбросим его таймер, чтобы он не запустился снова
|
||||
if (this.nSendLoopTimeOut) {
|
||||
clearTimeout(this.nSendLoopTimeOut);
|
||||
this.nSendLoopTimeOut = null;
|
||||
}
|
||||
//Выставляем флаг - не надо оповещать об останове
|
||||
this.bNotifyStopped = false;
|
||||
//Оповестим об останове
|
||||
this.notifyStopped();
|
||||
} else {
|
||||
//Выставляем флаг необходимости оповещения об останове (это будет сделано автоматически по завершению цикла проверки)
|
||||
this.bNotifyStopped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------
|
||||
// Интерфейс модуля
|
||||
//-----------------
|
||||
|
||||
exports.SEVT_NOTIFIER_STARTED = SEVT_NOTIFIER_STARTED;
|
||||
exports.SEVT_NOTIFIER_STOPPED = SEVT_NOTIFIER_STOPPED;
|
||||
exports.Notifier = Notifier;
|
83
models/prms_notifier.js
Normal file
83
models/prms_notifier.js
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Сервис интеграции ПП Парус 8 с WEB API
|
||||
Модели данных: описатели параметров функций модуля рассылки уведомлений (класс Notifier)
|
||||
*/
|
||||
|
||||
//----------------------
|
||||
// Подключение библиотек
|
||||
//----------------------
|
||||
|
||||
const Schema = require("validate"); //Схемы валидации
|
||||
const { mail } = require("./obj_config"); //Схемы валидации конфигурации сервера приложений
|
||||
const { Logger } = require("../core/logger"); //Класс для протоколирования работы
|
||||
const { validateMailList } = require("./common"); //Общие объекты валидации моделей данных
|
||||
|
||||
//-------------
|
||||
// Тело модуля
|
||||
//-------------
|
||||
|
||||
//Валидация списка адресов E-Mail для отправки уведомления
|
||||
const validateTo = val => {
|
||||
return validateMailList(val);
|
||||
};
|
||||
|
||||
//------------------
|
||||
// Интерфейс модуля
|
||||
//------------------
|
||||
|
||||
//Схема валидации параметров конструктора
|
||||
exports.Notifier = new Schema({
|
||||
//Параметры отправки E-Mail уведомлений
|
||||
mail: {
|
||||
schema: mail,
|
||||
required: true,
|
||||
message: {
|
||||
required: path => `Не указаны параметры отправки E-Mail уведомлений (${path})`
|
||||
}
|
||||
},
|
||||
//Объект для протоколирования работы
|
||||
logger: {
|
||||
type: Logger,
|
||||
required: true,
|
||||
message: {
|
||||
type: path =>
|
||||
`Объект для протоколирования работы (${path}) имеет некорректный тип данных (ожидалось - Logger)`,
|
||||
required: path => `Не указаны объект для протоколирования работы (${path})`
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Схема валидации параметров функции добавления сообщения в очередь отправки
|
||||
exports.addMessage = new Schema({
|
||||
//Список адресов E-Mail для отправки уведомления
|
||||
sTo: {
|
||||
type: String,
|
||||
required: true,
|
||||
use: { validateTo },
|
||||
message: {
|
||||
type: path =>
|
||||
`Список адресов E-Mail для отправки уведомления (${path}) имеет некорректный тип данных (ожидалось - String)`,
|
||||
required: path => `Не указан cписок адресов E-Mail для отправки уведомления (${path})`,
|
||||
validateTo: path =>
|
||||
`Неверный формат списка адресов E-Mail для отправки уведомления (${path}), для указания нескольких адресов следует использовать запятую в качестве разделителя (без пробелов)`
|
||||
}
|
||||
},
|
||||
//Заголовок сообщения
|
||||
sSubject: {
|
||||
type: String,
|
||||
required: true,
|
||||
message: {
|
||||
type: path => `Заголовок сообщения (${path}) имеет некорректный тип данных (ожидалось - String)`,
|
||||
required: path => `Не указан заголовок сообщения (${path})`
|
||||
}
|
||||
},
|
||||
//Текст уведомления
|
||||
sMessage: {
|
||||
type: String,
|
||||
required: true,
|
||||
message: {
|
||||
type: path => `Текст уведомления (${path}) имеет некорректный тип данных (ожидалось - String)`,
|
||||
required: path => `Не указан текст уведомления (${path})`
|
||||
}
|
||||
}
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user