// Reseta atendimentos já avaliados (Avaliavel=true) para pendente, para que a próxima // rodada de avaliação também extraia StatusCancelamento/MotivoCancelamento (campos novos). // Não faz a reavaliação em si — só o reset. Depois de rodar com --apply, drenar a fila // com scripts/avaliarTudo.js (idempotente entre reinícios, ao contrário deste script). // // Uso: node scripts/backfillCancelamento.js [--dias 90] [--apply] [--limite N] // --dias N : só atendimentos com Abertura nos últimos N dias (omitir = todo o histórico Avaliavel=true) // --apply : executa de fato (sem essa flag, é dry-run) // --limite N : limita quantos IDs entram no backfill (teste) import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { AtendimentoAvaliacao } from "../src/models/AtendimentoAvaliacao.model.js"; import { contarPendentes } from "../src/services/atendimentoAvaliacaoService.js"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const APLICAR = process.argv.includes("--apply"); const diasIdx = process.argv.indexOf("--dias"); const DIAS = diasIdx > -1 ? Number(process.argv[diasIdx + 1]) : null; const limiteIdx = process.argv.indexOf("--limite"); const LIMITE = limiteIdx > -1 ? Number(process.argv[limiteIdx + 1]) : null; async function buscarCandidatos() { const query = AtendimentoAvaliacao.query() .where("Avaliavel", true) .join("atendimentos as a", "a.Id", "atendimento_avaliacoes.AtendimentoId") .select( "atendimento_avaliacoes.AtendimentoId", "atendimento_avaliacoes.StatusCancelamento", "atendimento_avaliacoes.MotivoCancelamento", "a.Abertura" ) .orderBy("a.Abertura", "desc"); if (DIAS) { const cutoff = new Date(Date.now() - DIAS * 24 * 60 * 60 * 1000); query.where("a.Abertura", ">=", cutoff); } return query; } async function main() { const candidatos = await buscarCandidatos(); const alvos = LIMITE ? candidatos.slice(0, LIMITE) : candidatos; console.log( `[backfill-cancelamento] ${candidatos.length} candidatos` + `${DIAS ? ` (Abertura nos últimos ${DIAS} dias)` : " (todo o histórico Avaliavel=true)"}.` ); console.log(`[backfill-cancelamento] ${alvos.length} avaliações serão resetadas para pendente${LIMITE ? ` (--limite ${LIMITE})` : ""}.`); if (!APLICAR) { console.log("[backfill-cancelamento] dry-run (nenhuma alteração feita). Use --apply para executar."); process.exit(0); } if (alvos.length === 0) { console.log("[backfill-cancelamento] nada para reavaliar."); process.exit(0); } const backupDir = path.resolve(__dirname, "logs"); fs.mkdirSync(backupDir, { recursive: true }); const backupPath = path.join(backupDir, `backfill-cancelamento-${new Date().toISOString().replace(/[:.]/g, "-")}.json`); fs.writeFileSync(backupPath, JSON.stringify(alvos, null, 2)); console.log(`[backfill-cancelamento] backup dos valores antigos salvo em ${backupPath}`); const ids = alvos.map((a) => a.AtendimentoId); const pendentesAntes = await contarPendentes(); await AtendimentoAvaliacao.query().whereIn("AtendimentoId", ids).patch({ UltimaMensagemId: null }); const pendentesDepois = await contarPendentes(); console.log(`[backfill-cancelamento] resetados ${ids.length} — fila de pendentes ${pendentesAntes} -> ${pendentesDepois}.`); console.log("[backfill-cancelamento] pronto. Agora rode scripts/avaliarTudo.js para drenar a fila."); process.exit(0); } main().catch((e) => { console.error("[backfill-cancelamento] falha:", e.message); process.exit(1); });