chore: adiciona Docker, scripts e documentacao

- Adiciona Dockerfile e docker-compose para containerizacao
- Adiciona docker-entrypoint.sh com inicializacao
- Adiciona scripts/seed-admin.js para criar admin inicial
- Adiciona docs/ com logos originais CambioReal
- Atualiza README.md com instrucoes de uso
- Atualiza queries.js com metricas de portfólio

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
root
2026-02-08 13:20:15 -05:00
parent 1321b949e4
commit 96222aa6a2
12 changed files with 1470 additions and 68 deletions

View File

@@ -94,4 +94,133 @@ function serialize(rowsBrlUsd, rowsUsdBrl) {
return [...dataBrlUsd, ...dataUsdBrl].sort((a, b) => a.data_sort.localeCompare(b.data_sort));
}
module.exports = { fetchTransacoes, serialize };
// Fetch ALL transactions (for admin) - with date filter for performance
async function fetchAllTransacoes(diasAtras = 90) {
const conn = await pool.getConnection();
try {
const [rowsBrlUsd] = await conn.execute(`
SELECT
c.nome AS cliente,
t.created_at AS data_operacao,
t.amount_brl AS valor_reais,
t.amount_usd AS valor_dolar,
t.iof AS iof_pct,
ROUND(t.iof / 100 * t.amount_usd * t.exchange_rate, 2) AS iof_valor_rs,
ROUND(t.ptax, 4) AS taxa_ptax,
ROUND(t.exchange_rate, 4) AS taxa_cobrada,
ROUND(t.exchange_rate - t.ptax, 4) AS spread_bruto,
ROUND((t.exchange_rate - t.ptax) / t.exchange_rate * 100, 2) AS spread_pct,
t.status
FROM br_transaction_to_usa t
INNER JOIN conta c ON c.id_conta = t.id_conta
WHERE t.created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
ORDER BY t.created_at DESC
`, [diasAtras]);
const [rowsUsdBrl] = await conn.execute(`
SELECT
c.nome AS cliente,
p.created_at AS data_operacao,
p.valor_sol AS valor_reais,
p.valor AS valor_dolar,
0 AS iof_pct,
0 AS iof_valor_rs,
ROUND(p.ptax, 4) AS taxa_ptax,
ROUND(p.cotacao, 4) AS taxa_cobrada,
ROUND(p.ptax - p.cotacao, 4) AS spread_bruto,
CASE WHEN p.cotacao > 0 THEN ROUND((p.ptax - p.cotacao) / p.ptax * 100, 2) ELSE 0 END AS spread_pct,
p.pgto AS status
FROM pagamento_br p
INNER JOIN conta c ON c.id_conta = p.id_conta
WHERE p.created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
ORDER BY p.created_at DESC
`, [diasAtras]);
return { rowsBrlUsd, rowsUsdBrl };
} finally {
conn.release();
}
}
// Fast daily stats for admin home (today and yesterday only)
// Separates USD->BRL (with cotacao) from USD->USD (balance/no cotacao)
async function fetchDailyStats() {
const conn = await pool.getConnection();
try {
// BRL -> USD counts and volumes for today and yesterday
const [brlUsdStats] = await conn.execute(`
SELECT
DATE(created_at) as dia,
COUNT(*) as qtd,
ROUND(SUM(amount_brl), 2) as total_brl,
ROUND(SUM(amount_usd), 2) as total_usd
FROM br_transaction_to_usa
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)
GROUP BY DATE(created_at)
ORDER BY dia DESC
`);
// USD -> BRL (with cotacao - real currency exchange)
const [usdBrlStats] = await conn.execute(`
SELECT
DATE(created_at) as dia,
COUNT(*) as qtd,
ROUND(SUM(valor_sol), 2) as total_brl,
ROUND(SUM(valor), 2) as total_usd
FROM pagamento_br
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND cotacao IS NOT NULL AND cotacao > 0
AND (pgto IS NULL OR pgto != 'balance')
GROUP BY DATE(created_at)
ORDER BY dia DESC
`);
// USD -> USD (balance or no cotacao - dollar to dollar)
const [usdUsdStats] = await conn.execute(`
SELECT
DATE(created_at) as dia,
COUNT(*) as qtd,
ROUND(SUM(valor), 2) as total_usd
FROM pagamento_br
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND (cotacao IS NULL OR cotacao = 0 OR pgto = 'balance')
GROUP BY DATE(created_at)
ORDER BY dia DESC
`);
// Format results
const today = new Date().toISOString().slice(0, 10);
const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
const formatDay = (stats, targetDate) => {
const row = stats.find(r => {
const d = r.dia instanceof Date ? r.dia.toISOString().slice(0, 10) : String(r.dia).slice(0, 10);
return d === targetDate;
});
return row ? {
qtd: Number(row.qtd),
total_brl: Number(row.total_brl) || 0,
total_usd: Number(row.total_usd) || 0
} : { qtd: 0, total_brl: 0, total_usd: 0 };
};
return {
brlUsd: {
hoje: formatDay(brlUsdStats, today),
ontem: formatDay(brlUsdStats, yesterday)
},
usdBrl: {
hoje: formatDay(usdBrlStats, today),
ontem: formatDay(usdBrlStats, yesterday)
},
usdUsd: {
hoje: formatDay(usdUsdStats, today),
ontem: formatDay(usdUsdStats, yesterday)
}
};
} finally {
conn.release();
}
}
module.exports = { fetchTransacoes, fetchAllTransacoes, serialize, fetchDailyStats };