Baseline commit of the existing Maquina Comercial Pessoal project

Tracks the app that was already in place (capture, treatment, outreach,
WhatsApp integration, server, frontend, tests, docs) so future feature
work can branch and use isolated worktrees.
This commit is contained in:
Gustavo
2026-07-31 10:54:07 -03:00
parent 6cd336a41e
commit d330bb5ff0
132 changed files with 29969 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
import { loadDotEnv } from '../src/env.mjs';
import { openDb, transaction } from '../src/db.mjs';
import { scoreQualidade, scoreComercial, scoreGeral, classificarScoreGeral } from '../src/scoring.mjs';
// Recalcula score_qualidade/score_comercial/score/classificacao/prioridade/
// produto_principal de TODOS os leads a partir dos dados ja gravados + da
// afinidade/produtos configurados por nicho (tabela segments). Nao e um
// script de uso unico: roda de novo (`npm run recalc-scores`) sempre que o
// catalogo de afinidade/produtos em prospeccao/config.json mudar.
//
// Roda com o servidor parado (evita concorrencia de escrita no WAL).
await loadDotEnv();
const BATCH_SIZE = 2000;
function produtoPrincipalDe(produtos) {
return produtos.length ? [...produtos].sort((a, b) => b.estrelas - a.estrelas)[0].nome : '';
}
async function main() {
const db = openDb();
const segmentos = new Map(
db.prepare('SELECT name, affinity, products_json FROM segments').all()
.map((row) => [row.name, { affinity: row.affinity, products: JSON.parse(row.products_json) }])
);
const update = db.prepare(`
UPDATE leads SET
score = ?, classificacao = ?, motivo_classificacao = ?,
score_qualidade = ?, score_comercial = ?, prioridade = ?,
produto_principal = ?, produtos_json = ?, updated_at = datetime('now')
WHERE id = ?
`);
const total = db.prepare('SELECT COUNT(*) AS t FROM leads').get().t;
const porPrioridade = { 1: 0, 2: 0, 3: 0, 4: 0 };
let processed = 0;
let lastId = 0;
const page = db.prepare('SELECT * FROM leads WHERE id > ? ORDER BY id LIMIT ?');
while (true) {
const rows = page.all(lastId, BATCH_SIZE);
if (!rows.length) break;
transaction(db, () => {
for (const lead of rows) {
const info = segmentos.get(lead.segmento) || { affinity: 'media', products: [] };
const qualidade = scoreQualidade({
whatsapp: lead.whatsapp, cnpj: lead.cnpj, empresa: lead.empresa,
cidade: lead.cidade, segmento: lead.segmento, cnae: lead.cnae, website: lead.website
});
const comercial = scoreComercial({
whatsapp: lead.whatsapp, cnpj: lead.cnpj, website: lead.website,
email: lead.email, affinity: info.affinity
});
const geral = scoreGeral(qualidade.score, comercial);
const tier = classificarScoreGeral(geral);
const produtoPrincipal = produtoPrincipalDe(info.products);
update.run(
geral, tier.id, `score geral ${geral} (qualidade ${qualidade.score}, comercial ${comercial})`,
qualidade.score, comercial, tier.prioridade,
produtoPrincipal, JSON.stringify(info.products),
lead.id
);
porPrioridade[tier.prioridade] += 1;
}
});
processed += rows.length;
lastId = rows[rows.length - 1].id;
console.log(`${processed}/${total} leads recalculados...`);
}
console.log('Concluido. Distribuicao por prioridade:', porPrioridade);
db.close();
}
main().catch((error) => {
console.error('Erro no recalculo de scores:', error);
process.exit(1);
});