/* Сервис интеграции ПП Парус 8 с WEB API Дополнительный модуль: работа с БД ПП Парус 8 (Postgre) */ //---------------------- // Подключение библиотек //---------------------- const pg = require("pg"); //Работа с СУБД Postgre //-------------------------- // Глобальные идентификаторы //-------------------------- const DT_VARCHAR = pg.types.builtins.VARCHAR; //Тип данных "Строка" БД const DT_NUMBER = pg.types.builtins.NUMERIC; //Тип данных "Число" БД const DT_DATE = pg.types.builtins.DATE; //Тип данных "Дата" БД const DT_CLOB = pg.types.builtins.TEXT; //Тип данных "Текстовые данные" БД const DT_BLOB = pg.types.builtins.BYTEA; //Тип данных "Двоичные данные" БД const DT_CURSOR = pg.types.builtins.REFCURSOR; //Тип данных "Курсор" БД //------------ // Тело модуля //------------ //Установка парсера значений для типа данных NUMERIC БД PG pg.types.setTypeParser(pg.types.builtins.NUMERIC, val => { return val ? Number(val) : val; }); //Формирование имени хранимого объекта БД (с параметрами) const makeStoredName = (sName, inPrms) => { let prms = ""; let prm_numb = 0; for (i in inPrms) { prm_numb++; let prm_type = ""; if (inPrms[i] != null && inPrms[i] != undefined) switch (typeof inPrms[i]) { case "string": if (inPrms[i].length ? inPrms[i].length > 4000 : false) prm_type = "::text"; else prm_type = "::character varying"; break; case "number": prm_type = "::numeric"; break; case "bigint": prm_type = "::numeric"; break; case "boolean": prm_type = "::boolean"; break; case "object": if (inPrms[i] instanceof Date) prm_type = "::date"; else prm_type = "::bytea"; break; default: break; } prms += `${prms ? ", " : ""}$${prm_numb}${prm_type}`; } return `${sName.replace(".", "$")}(${prms})`; }; //Формирование параметров хранимого объекта БД const makeStoredPrms = inPrms => { let prms = []; for (i in inPrms) prms.push(inPrms[i]); return prms; }; //Исполнение хранимого объекта БД const executeStored = async prms => { let client; let outResult = {}; try { client = await prms.connection.connect(); let outPrmName = ""; let outPrmIsCursor = false; for (i in prms.outPrms) { outPrmName = i; outPrmIsCursor = prms.outPrms[i] == DT_CURSOR; break; } await client.query(`begin;`); let res = await client.query({ text: `select ${makeStoredName(prms.sName, prms.inPrms)}${outPrmName ? ` as "${outPrmName}"` : ""};`, values: makeStoredPrms(prms.inPrms) }); if (res) if (res.rows) if (res.rows[0]) { const outBind = res.rows[0][outPrmName]; if (outPrmIsCursor) { let rowsRes = []; let doExit = false; while (!doExit) { doExit = true; res = await client.query(`fetch next from "${outBind}";`); if (res) if (res.rows) if (res.rowCount > 0 && res.rows.length > 0) { doExit = false; res.rows.map(r => rowsRes.push(r)); } } outResult[outPrmName] = rowsRes; } else outResult[outPrmName] = outBind; } await client.query(`commit;`); } catch (e) { if (client) { try { await client.query(`rollback;`); } catch (err) { throw new Error(err.message); } } throw new Error(e.message); } finally { if (client) { try { await client.release(); } catch (err) { throw new Error(err.message); } } } if (outResult) return outResult; }; //Проверка условий при запуске сервиса интеграции const checkAppStart = async prms => { let res = await executeStored({ connection: prms.connection, sName: "PKG_EXS$UTL_APPSRV_START_CHECK", inPrms: { NCONTROL_VERSION: prms.bControlSystemVersion ? 1 : 0, SEXS_RELEASE_DATE: prms.sRelease, NWORKERS: prms.nWorkers, SEXSSRV: prms.sServerName !== "" ? prms.sServerName : null, SSESSION_APP_NAME: prms.sSessionAppName }, outPrms: { SERR_TEXT: DT_VARCHAR } }); //Если есть ошибки запуска if (res.SERR_TEXT) { throw new Error(res.SERR_TEXT); } }; //Инициализация сервера интеграции const initServer = async prms => { let res = await executeStored({ connection: prms.connection, sName: "PKG_EXS$EXSSRV_INIT", inPrms: { SEXSSRV: prms.sServerName, SIP: prms.sServerIP, NWORKERS: prms.nMaxWorkers }, outPrms: { NEXSSRV: DT_NUMBER } }); //Если рег. номер сервера интеграции потерялся if (!res.NEXSSRV) { throw new Error(`Ошибка считывания сервера интеграции с мнемокодом "${prms.sServerName}".`); } //Возвращаем рег. номер сервера интеграции return res.NEXSSRV; }; //Очистка информации о сервере при закрытии сервиса const clearServer = async prms => { await executeStored({ connection: prms.connection, sName: "PKG_EXS$EXSSRV_CLEAR", inPrms: { NEXSSRV: prms.nExsSrv } }); }; //Подключение к БД const connect = async prms => { try { //Создаем пул подключения let pool = new pg.Pool({ connectionString: `postgres://${prms.sUser}:${prms.sPassword}@${prms.sConnectString}`, connectionTimeoutMillis: 600000, min: prms.nPoolMin ? prms.nPoolMin : 4, max: prms.nPoolMax ? prms.nPoolMax : 4 }); pool.on("acquire", async client => { //Устанавливаем схему await client.query(`select ALTER_SESSION_SET_SCHEMA($1);`, [prms.sSchema]); //Устанавливаем модуль сессии await client.query(`select PKG_EXS$UTL_MODULE_SET($1);`, [prms.exsSrv.sServerName]); }); return pool; } catch (e) { throw new Error(e.message); } }; //Отключение от БД const disconnect = async prms => { try { await prms.connection.end(); } catch (e) { throw new Error(e.message); } }; //Получение списка сервисов const getServices = async prms => { let servicesData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$SERVICES_GET", inPrms: { NEXSSRV: prms.nExsSrv }, outPrms: { RCSERVICES: DT_CURSOR } }); return servicesData.RCSERVICES; }; //Получение списка функций сервиса const getServiceFunctions = async prms => { let serviceFunctionsData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$SERVICEFNS_GET", inPrms: { NEXSSERVICE: prms.nServiceId }, outPrms: { RCSERVICEFNS: DT_CURSOR } }); return serviceFunctionsData.RCSERVICEFNS; }; //Получение контекста сервиса const getServiceContext = async prms => { let serviceContextData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$SERVICE_CTX_GET", inPrms: { NFLAG_SMART: 0, NEXSSERVICE: prms.nServiceId }, outPrms: { RCSERVICE_CTX: DT_CURSOR } }); return serviceContextData.RCSERVICE_CTX[0]; }; //Установка контекста сервиса const setServiceContext = async prms => { await executeStored({ connection: prms.connection, sName: "PKG_EXS$SERVICE_CTX_SET", inPrms: { NEXSSERVICE: prms.nServiceId, SCTX: prms.sCtx, DCTX_EXP: prms.dCtxExp } }); }; //Очистка контекста сервиса const clearServiceContext = async prms => { await executeStored({ connection: prms.connection, sName: "PKG_EXS$SERVICE_CTX_CLEAR", inPrms: { NEXSSERVICE: prms.nServiceId } }); }; //Проверка атуентифицированности сервиса const isServiceAuth = async prms => { let serviceAuth = await executeStored({ connection: prms.connection, sName: "PKG_EXS$SERVICE_IS_AUTH", inPrms: { NEXSSERVICE: prms.nServiceId }, outPrms: { RET: DT_NUMBER } }); return serviceAuth.RET; }; //Постановка в очередь задания на аутентификацию сервиса const putServiceAuthInQueue = async prms => { await executeStored({ connection: prms.connection, sName: "PKG_EXS$SERVICE_AUTH_PUT_INQUEUE_AT", inPrms: { NEXSSERVICE: prms.nServiceId, NFORCE: prms.nForce } }); }; //Получение информации о просроченных сообщениях обмена сервиса const getServiceExpiredQueueInfo = async prms => { let serviceExpiredQueueInfoData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$SERVICE_QUEUE_EXPIRED_INFO_GET", inPrms: { NEXSSERVICE: prms.nServiceId }, outPrms: { RCSERVICE_QUEUE_EXPIRED_INFO: DT_CURSOR } }); return serviceExpiredQueueInfoData.RCSERVICE_QUEUE_EXPIRED_INFO[0]; }; //Запись в протокол работы const log = async prms => { let logData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$LOG_PUT", inPrms: { NLOG_STATE: prms.nLogState, SMSG: prms.sMsg, NEXSSERVICE: prms.nServiceId, NEXSSERVICEFN: prms.nServiceFnId, NEXSQUEUE: prms.nQueueId, NEXSSRV: prms.nExsSrv }, outPrms: { RCLOG: DT_CURSOR } }); return logData.RCLOG[0]; }; //Считывание записи очереди обмена const getQueue = async prms => { let queueData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_GET", inPrms: { NFLAG_SMART: 0, NEXSQUEUE: prms.nQueueId }, outPrms: { RCQUEUE: DT_CURSOR } }); return queueData.RCQUEUE[0]; }; //Помещение сообщения в очередь const putQueue = async prms => { let queueData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_PUT$2", inPrms: { NEXSSERVICEFN: prms.nServiceFnId, BMSG: prms.blMsg, NEXSQUEUE: prms.nQueueId, NLNK_COMPANY: prms.nLnkCompanyId, NLNK_DOCUMENT: prms.nLnkDocumentId, SLNK_UNITCODE: prms.sLnkUnitcode, SOPTIONS: prms.sOptions }, outPrms: { RCQUEUE: DT_CURSOR } }); return queueData.RCQUEUE[0]; }; //Считывание очередной порции исходящих сообщений из очереди const getQueueOutgoing = async prms => { let queueOutgoingData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_SRV_TYPE_SEND_GET", inPrms: { NPORTION_SIZE: prms.nPortionSize, NEXSSRV: prms.nExsSrv }, outPrms: { RCQUEUES: DT_CURSOR } }); return queueOutgoingData.RCQUEUES; }; //Установка значения состояния в сообщении очереди const setQueueState = async prms => { let queueStateData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_EXEC_STATE_SET", inPrms: { NEXSQUEUE: prms.nQueueId, NEXEC_STATE: prms.nExecState, SEXEC_MSG: prms.sExecMsg, NINC_EXEC_CNT: prms.nIncExecCnt, NRESET_DATA: prms.nResetData }, outPrms: { RCQUEUE: DT_CURSOR } }); return queueStateData.RCQUEUE[0]; }; //Считывание данных сообщения очереди const getQueueMsg = async prms => { let queueMsgData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_MSG_GET", inPrms: { NEXSQUEUE: prms.nQueueId }, outPrms: { RCQUEUE_MSG: DT_CURSOR } }); return queueMsgData.RCQUEUE_MSG[0]; }; //Установка данных сообщения очереди const setQueueMsg = async prms => { let queueMsgData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_MSG_SET$2", inPrms: { NEXSQUEUE: prms.nQueueId, BMSG: prms.blMsg }, outPrms: { RCQUEUE: DT_CURSOR } }); return queueMsgData.RCQUEUE[0]; }; //Установка параметров сообщения очереди const setQueueOptions = async prms => { let queueOptionsData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_OPTIONS_SET$2", inPrms: { NEXSQUEUE: prms.nQueueId, SOPTIONS: prms.sOptions }, outPrms: { RCQUEUE: DT_CURSOR } }); return queueOptionsData.RCQUEUE[0]; }; //Считывание результата обработки сообщения очереди const getQueueResp = async prms => { let queueRespData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_RESP_GET", inPrms: { NEXSQUEUE: prms.nQueueId }, outPrms: { RCQUEUE_RESP: DT_CURSOR } }); return queueRespData.RCQUEUE_RESP[0]; }; //Установка результата обработки сообщения очереди const setQueueResp = async prms => { let queueRespData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_RESP_SET$2", inPrms: { NEXSQUEUE: prms.nQueueId, BRESP: prms.blResp, NIS_ORIGINAL: prms.nIsOriginal }, outPrms: { RCQUEUE: DT_CURSOR } }); return queueRespData.RCQUEUE[0]; }; //Установка параметров результата обработки сообщения очереди const setQueueOptionsResp = async prms => { let queueOptionsRespData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_OPTIONS_RESP_SET$2", inPrms: { NEXSQUEUE: prms.nQueueId, SOPTIONS_RESP: prms.sOptionsResp }, outPrms: { RCQUEUE: DT_CURSOR } }); return queueOptionsRespData.RCQUEUE[0]; }; //Исполнение обработчика со стороны БД для сообщения очереди const execQueuePrc = async prms => { let queuePrcData = await executeStored({ connection: prms.connection, sName: "PKG_EXS$QUEUE_PRC", inPrms: { NEXSQUEUE: prms.nQueueId }, outPrms: { RCRESULT: DT_CURSOR } }); return queuePrcData.RCRESULT[0]; }; //----------------- // Интерфейс модуля //----------------- exports.DT_VARCHAR = DT_VARCHAR; exports.DT_NUMBER = DT_NUMBER; exports.DT_DATE = DT_DATE; exports.DT_CLOB = DT_CLOB; exports.DT_BLOB = DT_BLOB; exports.DT_CURSOR = DT_CURSOR; exports.executeStored = executeStored; exports.checkAppStart = checkAppStart; exports.initServer = initServer; exports.clearServer = clearServer; exports.connect = connect; exports.disconnect = disconnect; exports.getServices = getServices; exports.getServiceFunctions = getServiceFunctions; exports.getServiceContext = getServiceContext; exports.setServiceContext = setServiceContext; exports.clearServiceContext = clearServiceContext; exports.isServiceAuth = isServiceAuth; exports.putServiceAuthInQueue = putServiceAuthInQueue; exports.getServiceExpiredQueueInfo = getServiceExpiredQueueInfo; exports.log = log; exports.getQueue = getQueue; exports.putQueue = putQueue; exports.getQueueOutgoing = getQueueOutgoing; exports.setQueueState = setQueueState; exports.getQueueMsg = getQueueMsg; exports.setQueueMsg = setQueueMsg; exports.setQueueOptions = setQueueOptions; exports.getQueueResp = getQueueResp; exports.setQueueResp = setQueueResp; exports.setQueueOptionsResp = setQueueOptionsResp; exports.execQueuePrc = execQueuePrc;