add bot wsp
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
import { InvoicesService } from '~/invoices/invoices.service';
|
||||
import { PrismaService } from '~/prisma/prisma.service';
|
||||
import { SessionsService } from '~/sessions/sessions.service';
|
||||
import { SystemConfigService } from '~/common/system-config.service';
|
||||
|
||||
@Injectable()
|
||||
export class SchedulingService {
|
||||
private readonly logger = new Logger(SchedulingService.name);
|
||||
private lastSessionCleanup: number = 0;
|
||||
private lastTokenCleanup: number = 0;
|
||||
|
||||
constructor(
|
||||
private readonly sessions: SessionsService,
|
||||
private readonly invoices: InvoicesService,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly systemConfig: SystemConfigService,
|
||||
) { }
|
||||
|
||||
// Desactiva sesiones vencidas cada 30 minutos (configurable)
|
||||
@Cron(CronExpression.EVERY_MINUTE, { name: 'deactivateExpiredSessions' })
|
||||
async deactivateExpiredSessionsJob() {
|
||||
// Verificar si debe ejecutarse según la configuración
|
||||
const intervalMinutes = this.systemConfig.getSessionCleanupInterval();
|
||||
const now = Date.now();
|
||||
|
||||
if (now - this.lastSessionCleanup < intervalMinutes * 60 * 1000) {
|
||||
return; // Aún no es tiempo de ejecutar
|
||||
}
|
||||
|
||||
this.lastSessionCleanup = now;
|
||||
|
||||
try {
|
||||
const count = await this.sessions.deactivateExpiredActives();
|
||||
if (count > 0) {
|
||||
this.logger.log(`Desactivadas ${count} sesiones vencidas.`);
|
||||
} else {
|
||||
this.logger.verbose('No había sesiones vencidas para desactivar.');
|
||||
}
|
||||
} catch (err: any) {
|
||||
this.logger.error('Error desactivando sesiones vencidas', err?.stack || err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Todos los meses se deben de generar las facturas y enlaces de pagos
|
||||
// para que el usuario pueda pagar la clase que este inscripto y activa.
|
||||
// Se ejecuta diariamente y verifica si es el día configurado del mes
|
||||
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT, { name: 'generateMonthlyInvoices' })
|
||||
async generateMonthlyInvoicesAndPaymentLinksJob() {
|
||||
const today = new Date().getDate();
|
||||
const configuredDay = this.systemConfig.getInvoiceGenerationDay();
|
||||
|
||||
// Solo ejecutar si es el día configurado del mes
|
||||
if (today !== configuredDay) {
|
||||
return;
|
||||
}
|
||||
|
||||
let totalProcessed = 0;
|
||||
let totalSuccess = 0;
|
||||
let totalFailed = 0;
|
||||
const errors: Array<{ sessionId: string; assistantId: string; error: string }> = [];
|
||||
|
||||
try {
|
||||
const sessions = await this.sessions.findAllSessionInThisMonth();
|
||||
this.logger.log(`Iniciando generación de facturas para ${sessions.length} sesiones`);
|
||||
|
||||
for (const session of sessions) {
|
||||
const { id: sessionId, assistants } = session;
|
||||
|
||||
for (const assistant of assistants) {
|
||||
totalProcessed++;
|
||||
|
||||
try {
|
||||
await this.invoices.newInvoiceForSession(
|
||||
assistant.id,
|
||||
sessionId,
|
||||
new Date()
|
||||
);
|
||||
totalSuccess++;
|
||||
this.logger.verbose(
|
||||
`✓ Factura creada: session=${sessionId}, assistant=${assistant.id}`
|
||||
);
|
||||
} catch (invoiceErr: any) {
|
||||
totalFailed++;
|
||||
const errorMsg = invoiceErr?.message || String(invoiceErr);
|
||||
|
||||
errors.push({
|
||||
sessionId,
|
||||
assistantId: assistant.id,
|
||||
error: errorMsg
|
||||
});
|
||||
|
||||
this.logger.warn(
|
||||
`✗ Error creando factura: session=${sessionId}, assistant=${assistant.id}, error=${errorMsg}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resumen final
|
||||
this.logger.log(
|
||||
`Generación de facturas completada: ` +
|
||||
`Total=${totalProcessed}, Éxito=${totalSuccess}, Fallos=${totalFailed}`
|
||||
);
|
||||
|
||||
if (errors.length > 0) {
|
||||
this.logger.error(
|
||||
`Se encontraron ${errors.length} errores durante la generación:`,
|
||||
JSON.stringify(errors, null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
} catch (err: any) {
|
||||
this.logger.error(
|
||||
'Error crítico generando facturas mensuales y enlaces de pago',
|
||||
err?.stack || err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Limpia tokens expirados cada hora (configurable)
|
||||
@Cron(CronExpression.EVERY_MINUTE, { name: 'cleanupExpiredTokens' })
|
||||
async cleanupExpiredTokensJob() {
|
||||
// Verificar si debe ejecutarse según la configuración
|
||||
const intervalMinutes = this.systemConfig.getTokenCleanupInterval();
|
||||
const now = Date.now();
|
||||
|
||||
if (now - this.lastTokenCleanup < intervalMinutes * 60 * 1000) {
|
||||
return; // Aún no es tiempo de ejecutar
|
||||
}
|
||||
|
||||
this.lastTokenCleanup = now;
|
||||
|
||||
try {
|
||||
const result = await this.prisma.token.deleteMany({
|
||||
where: {
|
||||
expiresAt: {
|
||||
lt: new Date(), // Eliminar los expirados
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (result.count > 0) {
|
||||
this.logger.log(`Eliminados ${result.count} tokens expirados`);
|
||||
} else {
|
||||
this.logger.verbose('No había tokens expirados para eliminar.');
|
||||
}
|
||||
} catch (err: any) {
|
||||
this.logger.error('Error limpiando tokens expirados', err?.stack || err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user