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:
131
src/queries.js
131
src/queries.js
@@ -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 };
|
||||
|
||||
Reference in New Issue
Block a user