App local (Node + SQLite) para captura, tratamento e exportacao de leads comerciais, com dashboard de gestao, login/usuarios e integracao Z-PRO. Co-Authored-By: Claude <noreply@anthropic.com>
94 lines
3.0 KiB
JavaScript
94 lines
3.0 KiB
JavaScript
import path from 'node:path';
|
|
import { loadDotEnv, paths } from '../env.mjs';
|
|
import { openDb, transaction } from '../db.mjs';
|
|
import { createJobIO } from '../jobs/protocol.mjs';
|
|
import { exportExcel } from './excel.mjs';
|
|
import { exportOportunidades, exportBulkDataInput } from './zpro.mjs';
|
|
|
|
// Processo filho de exportacao: gera os arquivos do tipo pedido e, ao final,
|
|
// numa unica transacao, registra os leads exportados (export_leads) e marca
|
|
// exported_at (para o filtro "ainda nao exportado" excluir esses leads).
|
|
|
|
await loadDotEnv();
|
|
|
|
const args = new Map(
|
|
process.argv.slice(2).map((arg) => {
|
|
const [key, value = 'true'] = arg.replace(/^--/, '').split('=');
|
|
return [key, value];
|
|
})
|
|
);
|
|
|
|
const io = createJobIO();
|
|
|
|
const GENERATORS = {
|
|
excel: exportExcel,
|
|
'zpro-oportunidades': exportOportunidades,
|
|
'zpro-bulk': exportBulkDataInput
|
|
};
|
|
|
|
async function main() {
|
|
const exportId = Number(args.get('export-id'));
|
|
if (!exportId) throw new Error('export-job: --export-id obrigatorio');
|
|
|
|
const db = openDb();
|
|
const row = db.prepare('SELECT * FROM exports WHERE id = ?').get(exportId);
|
|
if (!row) throw new Error(`export #${exportId} nao encontrado`);
|
|
|
|
const generator = GENERATORS[row.tipo];
|
|
if (!generator) throw new Error(`tipo de exportacao desconhecido: ${row.tipo}`);
|
|
|
|
const filtro = JSON.parse(row.filtro_json || '{}');
|
|
const params = JSON.parse(row.params_json || '{}');
|
|
|
|
db.prepare("UPDATE exports SET status = 'running' WHERE id = ?").run(exportId);
|
|
io.emit({ phase: row.tipo, message: 'gerando arquivos...' });
|
|
|
|
const dir = path.join(paths.exports, `export-${exportId}`);
|
|
const baseName = params.baseName || `leads-${row.tipo}`;
|
|
const leadIds = [];
|
|
|
|
try {
|
|
const result = await generator({
|
|
db,
|
|
filtro,
|
|
params,
|
|
dir,
|
|
baseName,
|
|
collectIds: leadIds,
|
|
onProgress: (p) => io.emit({ phase: row.tipo, ...p })
|
|
});
|
|
|
|
const marcar = params.marcarExportado !== false;
|
|
io.emit({ phase: row.tipo, message: `registrando ${leadIds.length} leads exportados...` });
|
|
|
|
transaction(db, () => {
|
|
const insertLink = db.prepare('INSERT OR IGNORE INTO export_leads (export_id, lead_id) VALUES (?, ?)');
|
|
const mark = marcar
|
|
? db.prepare("UPDATE leads SET exported_at = COALESCE(exported_at, datetime('now')), updated_at = datetime('now') WHERE id = ?")
|
|
: null;
|
|
|
|
for (const leadId of leadIds) {
|
|
insertLink.run(exportId, leadId);
|
|
if (mark) mark.run(leadId);
|
|
}
|
|
|
|
db.prepare(
|
|
"UPDATE exports SET status = 'done', total_leads = ?, arquivos_json = ?, finished_at = datetime('now') WHERE id = ?"
|
|
).run(result.total, JSON.stringify(result.files), exportId);
|
|
});
|
|
|
|
io.done({ total: result.total, arquivos: result.files });
|
|
} catch (error) {
|
|
db.prepare("UPDATE exports SET status = 'error', error = ?, finished_at = datetime('now') WHERE id = ?")
|
|
.run(String(error.message || error), exportId);
|
|
throw error;
|
|
} finally {
|
|
db.close();
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
io.fail(error);
|
|
process.exit(1);
|
|
});
|