REFACTOR: Trocado a biblioteca para tratamento de xlsx

This commit is contained in:
Rafael Alves Lopes 2026-05-13 08:28:09 -03:00
parent a3245592c8
commit 4c7d5e33d0
3 changed files with 886 additions and 0 deletions

805
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"csv-parser": "^3.0.0", "csv-parser": "^3.0.0",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"exceljs": "^4.4.0",
"express": "^4.18.2", "express": "^4.18.2",
"express-session": "^1.18.2", "express-session": "^1.18.2",
"fast-csv": "^4.3.6", "fast-csv": "^4.3.6",

View File

@ -2,6 +2,7 @@ const { consultarViabilidade, discoverDataType } = require('./viabilidadeService
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const XLSX = require('xlsx'); const XLSX = require('xlsx');
const ExcelJS = require('exceljs');
const { once } = require('events'); const { once } = require('events');
const { const {
incrementProcessed, incrementProcessed,
@ -45,6 +46,14 @@ function isExcelFile(filePath) {
return isZipBasedXlsx || isOleBasedXls || isHtmlExcel; return isZipBasedXlsx || isOleBasedXls || isHtmlExcel;
} }
function isXlsxFile(filePath) {
const ext = path.extname(filePath).toLowerCase();
if (ext === '.xlsx') return true;
const fileStart = fs.readFileSync(filePath).subarray(0, 4);
return fileStart[0] === 0x50 && fileStart[1] === 0x4b;
}
function detectDelimiter(line) { function detectDelimiter(line) {
const delimiters = [';', '\t', ',']; const delimiters = [';', '\t', ','];
return delimiters return delimiters
@ -247,6 +256,70 @@ function buildErrorResultColumns(err) {
return ['', '', '', '', cleanCsvValue(formatApiErrorResponse(err))]; return ['', '', '', '', cleanCsvValue(formatApiErrorResponse(err))];
} }
function cloneCellStyle(cell) {
return {
numFmt: cell.numFmt,
font: cell.font ? { ...cell.font } : undefined,
alignment: cell.alignment ? { ...cell.alignment } : undefined,
border: cell.border ? { ...cell.border } : undefined,
fill: cell.fill ? { ...cell.fill } : undefined,
protection: cell.protection ? { ...cell.protection } : undefined
};
}
function styleInsertedResultColumns(worksheet, headerRowNumber) {
RESULT_HEADERS.forEach((header, index) => {
const columnNumber = index + 1;
const sourceColumn = worksheet.getColumn(RESULT_HEADERS.length + 1);
const targetColumn = worksheet.getColumn(columnNumber);
targetColumn.width = Math.max(16, sourceColumn.width || 0);
const headerCell = worksheet.getRow(headerRowNumber).getCell(columnNumber);
const sourceHeaderCell = worksheet.getRow(headerRowNumber).getCell(RESULT_HEADERS.length + 1);
headerCell.value = header;
headerCell.style = cloneCellStyle(sourceHeaderCell);
});
}
async function processXlsxFile(jobId, inputPath, outputPath, rows, headerRowIndex, indexes) {
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.readFile(inputPath);
const worksheet = workbook.worksheets[0];
const headerRowNumber = headerRowIndex + 1;
worksheet.spliceColumns(1, 0, ...RESULT_HEADERS.map(() => []));
styleInsertedResultColumns(worksheet, headerRowNumber);
for (let rowIndex = headerRowIndex + 1; rowIndex < rows.length; rowIndex++) {
const cols = rows[rowIndex];
const geoPayload = buildGeoPayload(cols, indexes);
const cepPayload = buildCepPayload(cols, indexes);
if (!geoPayload && !cepPayload) continue;
const row = worksheet.getRow(rowIndex + 1);
try {
const viab = await consultarComFallback(geoPayload, cepPayload);
buildSuccessResultColumns(viab).forEach((value, index) => {
const cell = row.getCell(index + 1);
cell.value = value;
cell.style = cloneCellStyle(row.getCell(RESULT_HEADERS.length + 1));
});
incrementProcessed(jobId);
} catch (err) {
buildErrorResultColumns(err).forEach((value, index) => {
const cell = row.getCell(index + 1);
cell.value = value;
cell.style = cloneCellStyle(row.getCell(RESULT_HEADERS.length + 1));
});
incrementErrors(jobId);
incrementProcessed(jobId);
}
row.commit();
}
await workbook.xlsx.writeFile(outputPath);
}
function shiftCellAddress(address, colOffset) { function shiftCellAddress(address, colOffset) {
const decoded = XLSX.utils.decode_cell(address); const decoded = XLSX.utils.decode_cell(address);
decoded.c += colOffset; decoded.c += colOffset;
@ -338,6 +411,13 @@ async function processCsvFile(jobId, inputPath, originalName) {
const outputPath = path.join(__dirname, '..', 'outputs', outputFilename); const outputPath = path.join(__dirname, '..', 'outputs', outputFilename);
fs.mkdirSync(path.dirname(outputPath), { recursive: true }); fs.mkdirSync(path.dirname(outputPath), { recursive: true });
if (isXlsxFile(inputPath)) {
await processXlsxFile(jobId, inputPath, outputPath, rows, headerRowIndex, indexes);
finishJob(jobId, path.basename(outputPath));
return outputPath;
}
if (isExcel) { if (isExcel) {
const workbook = XLSX.readFile(inputPath, { cellDates: false, raw: false, cellStyles: true }); const workbook = XLSX.readFile(inputPath, { cellDates: false, raw: false, cellStyles: true });
const firstSheetName = workbook.SheetNames[0]; const firstSheetName = workbook.SheetNames[0];