303 lines
12 KiB
JavaScript
303 lines
12 KiB
JavaScript
/*
|
||
Сервис интеграции ПП Парус 8 с WEB API
|
||
Дополнительный модуль: Интеграция с ЭДО "СБИС" (SBIS)
|
||
*/
|
||
|
||
//------------------------------
|
||
// Подключение внешних библиотек
|
||
//------------------------------
|
||
|
||
const xml2js = require("xml2js"); //Конвертация XML в JSON и JSON в XML
|
||
const _ = require("lodash"); //Работа с коллекциями и объектами
|
||
const rqp = require("request-promise"); //Работа с HTTP/HTTPS запросами
|
||
const { SMCHD_STORAGE_SYSTEM } = require("./sbis_config"); //Система хранения МЧД
|
||
|
||
//---------------------
|
||
// Глобальные константы
|
||
//---------------------
|
||
|
||
// Список тегов которые должны содержать массив
|
||
const tag = [
|
||
"Вложение",
|
||
"Редакция",
|
||
"ДокументОснование",
|
||
"ДокументСледствие",
|
||
"Подпись",
|
||
"Событие",
|
||
"Этап",
|
||
"Действие",
|
||
"Сертификат",
|
||
"Доверенность"
|
||
];
|
||
|
||
// Список имен тегов для замены ([Старое значение], [Новое значение])
|
||
const replaceTags = [
|
||
['"Иные получатели":', '"ИныеПолучатели":'],
|
||
['"Создатель документа":', '"СоздательДокумента":'],
|
||
];
|
||
|
||
//------------
|
||
// Тело модуля
|
||
//------------
|
||
|
||
//Замена наименований тегов (для корректной работы toXML)
|
||
const replaceTag = (obj) => {
|
||
for (let value of replaceTags) {
|
||
obj = obj.replace(new RegExp(value[0], 'g'), value[1]);
|
||
}
|
||
return obj;
|
||
};
|
||
|
||
//Обернуть содержимое тега в массив
|
||
const toArray = (obj, tags) => {
|
||
for (const prop in obj) {
|
||
const value = obj[prop];
|
||
if (tags.indexOf(prop) != -1 && !_.isArray(obj[prop])) {
|
||
obj[prop] = JSON.parse("[" + JSON.stringify(value) + "]");
|
||
}
|
||
if (typeof value === "object") {
|
||
toArray(value, tag);
|
||
}
|
||
}
|
||
return obj;
|
||
};
|
||
|
||
//Конвертация в XML
|
||
const toXML = obj => {
|
||
const builder = new xml2js.Builder();
|
||
return builder.buildObject(obj);
|
||
};
|
||
|
||
//Конвертация в JSON
|
||
const parseXML = xmlDoc => {
|
||
return new Promise((resolve, reject) => {
|
||
xml2js.parseString(xmlDoc, { explicitArray: false, mergeAttrs: true }, (err, result) => {
|
||
if (err) reject(err);
|
||
else resolve(result);
|
||
});
|
||
});
|
||
};
|
||
|
||
//Конвертация в JSON
|
||
const toJSON = async obj => {
|
||
let result = await parseXML(obj);
|
||
result = result.root;
|
||
toArray(result, tag);
|
||
return result;
|
||
};
|
||
|
||
//Добавление определённого количетсва часов к дате
|
||
const addHours = (dDate, nHours) => {
|
||
dDate.setTime(dDate.getTime() + nHours * 60 * 60 * 1000);
|
||
return new Date(dDate);
|
||
};
|
||
|
||
//Обработчик "До" подключения к сервису
|
||
const beforeConnect = async prms => {
|
||
//Подготовим параметры аутентификации
|
||
const prmAtribute = "Параметр";
|
||
const loginAtribute = "Логин";
|
||
const passAtribute = "Пароль";
|
||
//Сформируем запрос на аутентификацию
|
||
return {
|
||
options: {
|
||
headers: {
|
||
"content-type": "application/json;charset=utf-8"
|
||
},
|
||
body: JSON.stringify({
|
||
jsonrpc: "2.0",
|
||
method: "СБИС.Аутентифицировать",
|
||
params: {
|
||
[prmAtribute]: {
|
||
[loginAtribute]: prms.service.sSrvUser,
|
||
[passAtribute]: prms.service.sSrvPass
|
||
}
|
||
},
|
||
id: 0
|
||
}),
|
||
simple: false
|
||
}
|
||
};
|
||
};
|
||
|
||
//Обработчик "После" подключения к сервису
|
||
const afterConnect = async prms => {
|
||
let resp = null;
|
||
//Разберем ответ
|
||
if (prms.queue.blResp) {
|
||
try {
|
||
resp = JSON.parse(prms.queue.blResp.toString());
|
||
} catch (e) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "СБИС". Ошибка интерпретации: ${e.message}`);
|
||
}
|
||
} else {
|
||
throw new Error('Сервер ЭДО "СБИС" не вернул ответ');
|
||
}
|
||
//Если в нём нет ошибок
|
||
if (!resp.error) {
|
||
//Сохраним полученный токен доступа в контекст сервиса
|
||
return {
|
||
blResp: Buffer.from(resp.result),
|
||
sCtx: resp.result,
|
||
dCtxExp: addHours(new Date(), 23)
|
||
};
|
||
} else {
|
||
throw new Error(`Сервер ЭДО "СБИС" вернул ошибку: ${resp.error.message ? resp.error.message : "Неожиданная ошибка"}`);
|
||
}
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса к сервису "СБИС"
|
||
const beforeDocParse = async prms => {
|
||
try {
|
||
//Считаем токен доступа из контекста сервиса
|
||
let sToken = null;
|
||
if (prms.service.sCtx) {
|
||
sToken = prms.service.sCtx;
|
||
}
|
||
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
|
||
if (!sToken) return { bUnAuth: true };
|
||
//Конвертируем XML из "Парус 8" в понятный "СБИСу" JSON
|
||
let obj = await toJSON(prms.queue.blMsg.toString());
|
||
//В зависимости от режима загрузки определим наименование узла
|
||
switch (obj.method) {
|
||
//Подготовка действия
|
||
case "СБИС.ПодготовитьДействие":
|
||
//Если требуется использовать МЧД
|
||
if (obj.params?.Документ?.Этап[0]?.Действие?.Сертификат?.Доверенность) {
|
||
obj.params.Документ.Этап[0].Действие.Сертификат.Доверенность.СистемаХраненияМЧД = SMCHD_STORAGE_SYSTEM;
|
||
}
|
||
break;
|
||
//Выполнение действия
|
||
case "СБИС.ВыполнитьДействие":
|
||
//Если требуется использовать МЧД
|
||
if (obj.params?.Документ?.Этап[0]?.Вложение?.Подпись?.Сертификат?.Доверенность) {
|
||
obj.params.Документ.Этап[0].Вложение.Подпись.Сертификат.Доверенность.СистемаХраненияМЧД = SMCHD_STORAGE_SYSTEM;
|
||
} else if (obj.params?.Документ?.Этап[0]?.Вложение[0]?.Подпись?.Сертификат?.Доверенность) {
|
||
for (i = 0; i < obj.params.Документ.Этап[0].Вложение.length; i++) {
|
||
obj.params.Документ.Этап[0].Вложение[i].Подпись.Сертификат.Доверенность.СистемаХраненияМЧД = SMCHD_STORAGE_SYSTEM;
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
}
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
headers: {
|
||
"Content-type": "application/json; charset=utf-8",
|
||
"X-SBISSessionID": sToken,
|
||
srv: 1
|
||
},
|
||
simple: false,
|
||
func: obj.method
|
||
},
|
||
blMsg: Buffer.from(JSON.stringify(obj))
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "После" запроса к сервису "СБИС"
|
||
const afterDocParse = async prms => {
|
||
//Преобразуем JSON ответ сервиса "СБИС" в XML, понятный "Парус 8"
|
||
let resu = null;
|
||
if (prms.queue.blResp) {
|
||
try {
|
||
resu = toXML(JSON.parse(replaceTag(prms.queue.blResp.toString())));
|
||
} catch (e) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "СБИС". Ошибка интерпретации: ${e.message}`);
|
||
}
|
||
} else {
|
||
throw new Error('Сервер ЭДО "СБИС" не вернул ответ');
|
||
}
|
||
//Возврат результата
|
||
return {
|
||
blResp: Buffer.from(resu)
|
||
};
|
||
};
|
||
|
||
//Обработчик "До" отправки запроса на загрузку вложения
|
||
const beforeAttParse = async prms => {
|
||
try {
|
||
//Считаем токен доступа из контекста сервиса
|
||
let sToken = null;
|
||
if (prms.service.sCtx) {
|
||
sToken = prms.service.sCtx;
|
||
}
|
||
//Если не достали из контекста токен доступа - значит нет аутентификации на сервере
|
||
if (!sToken) return { bUnAuth: true };
|
||
//Собираем и отдаём общий результат работы
|
||
return {
|
||
options: {
|
||
headers: {
|
||
"Content-type": "application/json; charset=utf-8",
|
||
"X-SBISSessionID": sToken,
|
||
srv: 1
|
||
},
|
||
simple: false
|
||
}
|
||
};
|
||
} catch (e) {
|
||
throw Error(e);
|
||
}
|
||
};
|
||
|
||
//Обработчик "После" отправки запроса на загрузку вложения
|
||
const afterAttParse = async prms => {
|
||
if (prms.queue.blResp) {
|
||
if (prms.optionsResp.statusCode == 200) {
|
||
return;
|
||
} else {
|
||
let iterable = [1, 2, 3, 4, 5];
|
||
//Если не превышает лимита запросов
|
||
for (let value of iterable) {
|
||
if (prms.optionsResp.statusCode != 200) {
|
||
//Выполним повторный запрос
|
||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||
let serverResp = await rqp(prms.options);
|
||
//Сохраняем полученный ответ
|
||
prms.queue.blResp = Buffer.from(serverResp.body || "");
|
||
prms.optionsResp.statusCode = serverResp.statusCode;
|
||
//Если пришел ответ
|
||
if (prms.queue.blResp && serverResp.statusCode == 200) {
|
||
//Вернем загруженный документ
|
||
return {
|
||
blResp: prms.queue.blResp
|
||
};
|
||
}
|
||
}
|
||
}
|
||
//Если был ответ от сервера с ошибкой (иначе мы сюда не попадём)
|
||
if (prms.queue.blResp) {
|
||
//Разберем сообщение об ошибке
|
||
let resu = null;
|
||
try {
|
||
resu = JSON.parse(prms.queue.blResp.toString());
|
||
} catch (e) {
|
||
throw new Error(`Неожиданный ответ сервера ЭДО "СБИС": ${prms.queue.blResp.toString()}`);
|
||
}
|
||
throw new Error(`Сервер ЭДО "СБИС" вернул ошибку: ${resu?.error?.message}`);
|
||
} else {
|
||
//Возврат результата
|
||
throw new Error('Сервер ЭДО "СБИС" не вернул ответ');
|
||
}
|
||
}
|
||
} else {
|
||
throw new Error('Сервер ЭДО "СБИС" не вернул ответ');
|
||
}
|
||
//Возврат результата
|
||
return;
|
||
};
|
||
|
||
//-----------------
|
||
// Интерфейс модуля
|
||
//-----------------
|
||
|
||
exports.beforeConnect = beforeConnect;
|
||
exports.afterConnect = afterConnect;
|
||
exports.beforeDocParse = beforeDocParse;
|
||
exports.afterDocParse = afterDocParse;
|
||
exports.beforeAttParse = beforeAttParse;
|
||
exports.afterAttParse = afterAttParse;
|