forked from CITKParus/P8-ExchangeService
550 lines
18 KiB
JavaScript
550 lines
18 KiB
JavaScript
/*
|
||
Сервис интеграции ПП Парус 8 с WEB API
|
||
Дополнительный модуль: работа с БД ПП Парус 8 (Oracle)
|
||
*/
|
||
|
||
//----------------------
|
||
// Подключение библиотек
|
||
//----------------------
|
||
|
||
const oracledb = require("oracledb"); //Работа с СУБД Oracle
|
||
|
||
//--------------------------
|
||
// Глобальные идентификаторы
|
||
//--------------------------
|
||
|
||
const NPOOL_DRAIN_TIME = 30; //Таймаут ожидания завершения подключений при отключении пула от БД (сек)
|
||
const DT_VARCHAR = oracledb.DB_TYPE_VARCHAR; //Тип данных "Строка" БД
|
||
const DT_NUMBER = oracledb.DB_TYPE_NUMBER; //Тип данных "Число" БД
|
||
const DT_DATE = oracledb.DB_TYPE_DATE; //Тип данных "Дата" БД
|
||
const DT_CLOB = oracledb.DB_TYPE_CLOB; //Тип данных "Текстовые данные" БД
|
||
const DT_BLOB = oracledb.DB_TYPE_BLOB; //Тип данных "Двоичные данные" БД
|
||
const DT_CURSOR = oracledb.CURSOR; //Тип данных "Курсор" БД
|
||
const DT_VARCHAR_LENGTH = 32767; //Длина типа "Строка"
|
||
|
||
//------------
|
||
// Тело модуля
|
||
//------------
|
||
|
||
//Формирование имени хранимого объекта БД (с параметрами)
|
||
const makeStoredName = (sName, inPrms, outPrms, isFunction = false) => {
|
||
let prms = "";
|
||
let resultVar = "";
|
||
for (i in inPrms) prms += `${prms ? ", " : ""}${i} => :${i}`;
|
||
for (i in outPrms) {
|
||
if (isFunction) resultVar = `:${i} := `;
|
||
else prms += `${prms ? ", " : ""}${i} => :${i}`;
|
||
break;
|
||
}
|
||
return `${resultVar ? resultVar : ""}${sName.replace(/\$[0-9]{1,9}$/, "").replace("$", ".")}(${prms})`;
|
||
};
|
||
|
||
//Формирование параметров хранимого объекта БД
|
||
const makeStoredPrms = (inPrms, outPrms) => {
|
||
let prms = inPrms ? inPrms : {};
|
||
for (i in outPrms) {
|
||
prms[i] = {
|
||
type: outPrms[i],
|
||
dir: oracledb.BIND_OUT,
|
||
...(outPrms[i] === DT_VARCHAR ? { maxSize: DT_VARCHAR_LENGTH } : {})
|
||
};
|
||
break;
|
||
}
|
||
return prms;
|
||
};
|
||
|
||
//Исполнение хранимого объекта БД
|
||
const executeStored = async prms => {
|
||
let pooledConnection;
|
||
let outResult = {};
|
||
try {
|
||
pooledConnection = await prms.connection.getConnection();
|
||
let outPrmName = "";
|
||
let outPrmIsCursor = false;
|
||
for (i in prms.outPrms) {
|
||
outPrmName = i;
|
||
outPrmIsCursor = prms.outPrms[i] == DT_CURSOR;
|
||
break;
|
||
}
|
||
let res = await pooledConnection.execute(
|
||
`begin ${makeStoredName(prms.sName, prms.inPrms, prms.outPrms, prms.isFunction)}; end;`,
|
||
makeStoredPrms(prms.inPrms, prms.outPrms),
|
||
{
|
||
...(outPrmIsCursor ? { outFormat: oracledb.OBJECT } : {}),
|
||
...{ autoCommit: true }
|
||
}
|
||
);
|
||
if (res)
|
||
if (res.outBinds) {
|
||
const outBind = res.outBinds[outPrmName];
|
||
if (outPrmIsCursor) {
|
||
const readCursorData = cursor => {
|
||
return new Promise((resolve, reject) => {
|
||
let queryStream = cursor.toQueryStream();
|
||
let rows = [];
|
||
queryStream.on("data", row => {
|
||
rows.push(row);
|
||
});
|
||
queryStream.on("error", err => {
|
||
queryStream.destroy();
|
||
reject(new Error(err.message));
|
||
});
|
||
queryStream.on("close", () => {
|
||
queryStream.destroy();
|
||
resolve(rows);
|
||
});
|
||
});
|
||
};
|
||
let rows = await readCursorData(outBind);
|
||
let rowsRes = [];
|
||
await Promise.all(
|
||
rows.map(async row => {
|
||
let rowRes = {};
|
||
for (i in row) {
|
||
let isLob = row[i] ? (row[i].type ? row[i].type == oracledb.BLOB || row[i].type == oracledb.CLOB : false) : false;
|
||
rowRes[i] = isLob ? await row[i].getData() : row[i];
|
||
}
|
||
rowsRes.push(rowRes);
|
||
})
|
||
);
|
||
outResult[outPrmName] = rowsRes;
|
||
} else outResult[outPrmName] = outBind;
|
||
}
|
||
} catch (e) {
|
||
throw new Error(e.message);
|
||
} finally {
|
||
if (pooledConnection) {
|
||
try {
|
||
await pooledConnection.close();
|
||
} catch (e) {
|
||
throw new Error(e.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,
|
||
SSESSION_APP_NAME: prms.sSessionAppName
|
||
},
|
||
outPrms: {
|
||
SERR_TEXT: DT_VARCHAR
|
||
},
|
||
isFunction: false
|
||
});
|
||
//Если есть ошибки запуска
|
||
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
|
||
},
|
||
isFunction: false
|
||
});
|
||
//Если рег. номер сервера интеграции не определен
|
||
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
|
||
},
|
||
isFunction: false
|
||
});
|
||
};
|
||
|
||
//Подключение к БД
|
||
const connect = async prms => {
|
||
try {
|
||
//Создаем пул подключения
|
||
let pool = await oracledb.createPool({
|
||
user: prms.sUser,
|
||
password: prms.sPassword,
|
||
connectString: prms.sConnectString,
|
||
queueTimeout: 600000,
|
||
poolMin: prms.nPoolMin ? prms.nPoolMin : 4,
|
||
poolMax: prms.nPoolMax ? prms.nPoolMax : 4,
|
||
poolIncrement: prms.nPoolIncrement ? prms.nPoolIncrement : 0,
|
||
sessionCallback: (connection, requestedTag, callback) => {
|
||
//Устанавливаем схему
|
||
connection
|
||
.execute(`ALTER SESSION SET CURRENT_SCHEMA=${prms.sSchema}`)
|
||
.then(r => {
|
||
//Устанавливаем модуль сессии
|
||
connection.execute(`begin PKG_EXS.UTL_MODULE_SET(SEXSSRV => '${prms.exsSrv.sServerName}'); end;`);
|
||
})
|
||
.then(r => {
|
||
callback(null, connection);
|
||
})
|
||
.catch(e => {
|
||
callback(e, null);
|
||
});
|
||
}
|
||
});
|
||
return pool;
|
||
} catch (e) {
|
||
throw new Error(e.message);
|
||
}
|
||
};
|
||
|
||
//Отключение от БД
|
||
const disconnect = async prms => {
|
||
try {
|
||
//Отключаем от базы данных
|
||
await prms.connection.close(NPOOL_DRAIN_TIME);
|
||
return;
|
||
} 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
|
||
},
|
||
isFunction: true
|
||
});
|
||
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",
|
||
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",
|
||
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",
|
||
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",
|
||
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",
|
||
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;
|