edumanagerpro2/manager/services/database.js

212 lines
6.1 KiB
JavaScript

/**
* ============================================================
* SERVIÇO DE BANCO DE DADOS — PostgreSQL (Self-Hosted)
* Substitui todas as chamadas supabase.from(...) do sistema
* ============================================================
*/
import pg from 'pg';
const DATABASE_URL = process.env.DATABASE_URL || 'postgresql://edumanager:EduManager2026!Seguro@postgres:5432/edumanager';
const pool = new pg.Pool({
connectionString: DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
pool.on('error', (err) => {
console.error('[PostgreSQL] Erro inesperado no pool:', err);
});
// ============================================================
// HELPER: Buscar school_data JSON blob (compatibilidade legada)
// ============================================================
export async function getSchoolData() {
const { rows } = await pool.query(
'SELECT data FROM school_data WHERE id = 1'
);
return rows[0]?.data || {};
}
export async function saveSchoolData(data) {
await pool.query(
`INSERT INTO school_data (id, data, updated_at)
VALUES (1, $1, NOW())
ON CONFLICT (id) DO UPDATE SET data = $1, updated_at = NOW()`,
[JSON.stringify(data)]
);
}
// ============================================================
// HELPERS: alunos_cobrancas
// ============================================================
export async function insertCobrancas(cobrancas) {
const client = await pool.connect();
try {
await client.query('BEGIN');
for (const c of cobrancas) {
await client.query(
`INSERT INTO alunos_cobrancas
(aluno_id, asaas_customer_id, asaas_payment_id, asaas_installment_id, installment, valor, vencimento, link_boleto)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
[c.aluno_id, c.asaas_customer_id, c.asaas_payment_id, c.asaas_installment_id || c.installment, c.installment, c.valor, c.vencimento, c.link_boleto]
);
}
await client.query('COMMIT');
} catch (e) {
await client.query('ROLLBACK');
throw e;
} finally {
client.release();
}
}
export async function updateCobranca(asaasPaymentId, updateData) {
const setClauses = [];
const values = [];
let i = 1;
for (const [key, value] of Object.entries(updateData)) {
if (value !== undefined) {
setClauses.push(`${key} = $${i}`);
values.push(value);
i++;
}
}
if (setClauses.length === 0) return;
values.push(asaasPaymentId);
await pool.query(
`UPDATE alunos_cobrancas SET ${setClauses.join(', ')} WHERE asaas_payment_id = $${i}`,
values
);
}
export async function deleteCobranca(asaasPaymentId) {
await pool.query(
'DELETE FROM alunos_cobrancas WHERE asaas_payment_id = $1',
[asaasPaymentId]
);
}
export async function getCobrancaByPaymentId(asaasPaymentId) {
const { rows } = await pool.query(
'SELECT * FROM alunos_cobrancas WHERE asaas_payment_id = $1',
[asaasPaymentId]
);
return rows[0] || null;
}
export async function getCobrancasByOrQuery(id) {
// Replicates: supabase.from('alunos_cobrancas').select('*').or(...)
const { rows } = await pool.query(
`SELECT * FROM alunos_cobrancas
WHERE installment = $1
OR asaas_installment_id = $1
OR asaas_payment_id = $1
OR id::text = $1
ORDER BY vencimento ASC`,
[id]
);
return rows;
}
export async function getCobrancasByAlunoId(alunoId) {
const { rows } = await pool.query(
'SELECT * FROM alunos_cobrancas WHERE aluno_id = $1 ORDER BY vencimento ASC',
[alunoId]
);
return rows;
}
export async function getCobrancasPendentes() {
const { rows } = await pool.query(
"SELECT * FROM alunos_cobrancas WHERE status = 'PENDENTE'"
);
return rows;
}
export async function getCobrancasAtrasadas() {
const { rows } = await pool.query(
"SELECT * FROM alunos_cobrancas WHERE status = 'ATRASADO'"
);
return rows;
}
export async function getCobrancasByInstallmentId(installmentId) {
const { rows } = await pool.query(
'SELECT * FROM alunos_cobrancas WHERE asaas_installment_id = $1 ORDER BY vencimento ASC',
[installmentId]
);
return rows;
}
export async function updateCobrancaLinkCarne(installmentId, linkCarne) {
await pool.query(
'UPDATE alunos_cobrancas SET link_carne = $1 WHERE asaas_installment_id = $2',
[linkCarne, installmentId]
);
}
export async function updateCobrancaByField(field, id, updateData) {
const setClauses = [];
const values = [];
let i = 1;
for (const [key, value] of Object.entries(updateData)) {
if (value !== undefined) {
setClauses.push(`${key} = $${i}`);
values.push(value);
i++;
}
}
if (setClauses.length === 0) return;
values.push(id);
await pool.query(
`UPDATE alunos_cobrancas SET ${setClauses.join(', ')} WHERE ${field} = $${i}`,
values
);
}
// ============================================================
// HELPERS: provas_submissoes
// ============================================================
export async function getSubmissoesByAluno(alunoId) {
const { rows } = await pool.query(
'SELECT * FROM provas_submissoes WHERE aluno_id = $1',
[alunoId]
);
return rows;
}
export async function getSubmissaoByAlunoAndExam(alunoId, examId) {
const { rows } = await pool.query(
'SELECT id FROM provas_submissoes WHERE aluno_id = $1 AND prova_id = $2 LIMIT 1',
[alunoId, examId]
);
return rows;
}
export async function insertSubmissao(submission) {
await pool.query(
`INSERT INTO provas_submissoes (aluno_id, prova_id, total_questoes, acertos, erros, percentual, nota_final, respostas, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
[
submission.aluno_id, submission.exam_id,
submission.total_questions, submission.correct_count, submission.wrong_count,
submission.percentage, submission.final_score,
JSON.stringify(submission.answers_json), submission.created_at
]
);
}
// ============================================================
// EXPORT POOL para queries diretas quando necessário
// ============================================================
export { pool };
export default pool;