import { getDb } from "./db"; import { eq, and, lte, sql } from "drizzle-orm"; import { questionnaires, sequences, inscriptions, apprenants, datesFormation, envoisQuestionnaires, } from "../drizzle/schema"; import { getAllQuestionnaires, checkEnvoiExists, createEnvoiQuestionnaire, } from "./questionnaireDb"; import { sendEmail } from "./_core/emailSender"; import crypto from "crypto"; /** * Génère un token unique pour accéder au questionnaire */ function generateToken(): string { return crypto.randomBytes(32).toString("hex"); } /** * Envoie un questionnaire à un apprenant pour une séquence donnée */ async function envoyerQuestionnaire( questionnaireId: number, apprenantId: number, sequenceId: number, apprenantEmail: string, apprenantNom: string, apprenantPrenom: string, questionnairenom: string, sequenceNom: string ) { // Vérifier si le questionnaire n'a pas déjà été envoyé const dejaEnvoye = await checkEnvoiExists(questionnaireId, apprenantId, sequenceId); if (dejaEnvoye) { console.log( `[Questionnaires] Questionnaire ${questionnaireId} déjà envoyé à l'apprenant ${apprenantId} pour la séquence ${sequenceId}` ); return false; } // Générer un token unique const token = generateToken(); // Créer l'envoi await createEnvoiQuestionnaire({ questionnaireId, apprenantId, sequenceId, token, dateEnvoi: new Date(), dateReponse: null, }); // Envoyer l'email const lienQuestionnaire = `${process.env.VITE_FRONTEND_URL || "http://localhost:3000"}/questionnaire/${token}`; const emailSent = await sendEmail({ to: apprenantEmail, subject: `Questionnaire : ${questionnairenom}`, html: `

Bonjour ${apprenantPrenom} ${apprenantNom},

Nous vous remercions d'avoir participé à la formation ${sequenceNom}.

Afin d'améliorer continuellement la qualité de nos formations, nous vous invitons à répondre à ce questionnaire :

Répondre au questionnaire

Ce questionnaire ne vous prendra que quelques minutes.

Merci pour votre participation !

Si le bouton ne fonctionne pas, copiez ce lien dans votre navigateur :
${lienQuestionnaire}

`, }); if (emailSent) { console.log( `[Questionnaires] Questionnaire ${questionnaireId} envoyé à ${apprenantEmail} pour la séquence ${sequenceId}` ); return true; } else { console.error( `[Questionnaires] Échec de l'envoi du questionnaire ${questionnaireId} à ${apprenantEmail}` ); return false; } } /** * Traite les envois automatiques de questionnaires post-formation */ export async function processEnvoisAutomatiques() { console.log("[Questionnaires] Démarrage du traitement des envois automatiques..."); const db = await getDb(); if (!db) { console.log("[Questionnaires] Base de données non disponible"); return { success: false, message: "Base de données non disponible" }; } try { // Récupérer tous les questionnaires actifs avec envoi automatique const questionnairesActifs = await getAllQuestionnaires(); const questionnairesAEnvoyer = questionnairesActifs.filter( (q) => q.actif && q.envoiAutomatique ); if (questionnairesAEnvoyer.length === 0) { console.log("[Questionnaires] Aucun questionnaire avec envoi automatique configuré"); return { success: true, message: "Aucun questionnaire à envoyer", count: 0 }; } let totalEnvoyes = 0; for (const questionnaire of questionnairesAEnvoyer) { console.log( `[Questionnaires] Traitement du questionnaire "${questionnaire.titre}" (délai: J+${questionnaire.delaiEnvoiJours})` ); // Calculer la date cible (aujourd'hui - délai) const dateCible = new Date(); dateCible.setDate(dateCible.getDate() - questionnaire.delaiEnvoiJours); dateCible.setHours(0, 0, 0, 0); // Récupérer les séquences terminées à la date cible const sequencesTerminees = await db .select({ sequenceId: sequences.id, sequenceNom: sequences.nom, formationId: sequences.formationId, }) .from(sequences) .innerJoin(datesFormation, eq(datesFormation.sequenceId, sequences.id)) .where(lte(datesFormation.dateFin, dateCible)) .groupBy(sequences.id); console.log( `[Questionnaires] ${sequencesTerminees.length} séquence(s) terminée(s) trouvée(s) pour le ${dateCible.toLocaleDateString("fr-FR")}` ); // Pour chaque séquence terminée for (const seq of sequencesTerminees) { // Vérifier si le questionnaire est lié à une formation spécifique if (questionnaire.formationId && questionnaire.formationId !== seq.formationId) { continue; } // Récupérer les apprenants inscrits et validés const apprenantsInscrits = await db .select({ apprenantId: apprenants.id, apprenantEmail: apprenants.email, apprenantNom: apprenants.nom, apprenantPrenom: apprenants.prenom, }) .from(inscriptions) .innerJoin(apprenants, eq(apprenants.id, inscriptions.apprenantId)) .where( and( eq(inscriptions.sequenceId, seq.sequenceId), eq(inscriptions.statut, "confirmee") ) ); console.log( `[Questionnaires] ${apprenantsInscrits.length} apprenant(s) inscrit(s) à la séquence ${seq.sequenceId}` ); // Envoyer le questionnaire à chaque apprenant for (const apprenant of apprenantsInscrits) { if (!apprenant.apprenantEmail) { console.log( `[Questionnaires] Apprenant ${apprenant.apprenantId} sans email, envoi ignoré` ); continue; } const envoye = await envoyerQuestionnaire( questionnaire.id, apprenant.apprenantId, seq.sequenceId, apprenant.apprenantEmail, apprenant.apprenantNom, apprenant.apprenantPrenom, questionnaire.titre, seq.sequenceNom ); if (envoye) { totalEnvoyes++; } } } } console.log(`[Questionnaires] Traitement terminé - ${totalEnvoyes} questionnaire(s) envoyé(s)`); return { success: true, message: `${totalEnvoyes} questionnaire(s) envoyé(s)`, count: totalEnvoyes, }; } catch (error: any) { console.error("[Questionnaires] Erreur lors du traitement :", error); return { success: false, message: error.message, count: 0 }; } } /** * Initialise le scheduler pour les envois automatiques de questionnaires * Exécute le traitement tous les jours à 9h00 */ export function initQuestionnaireScheduler() { console.log("[Questionnaires] Initialisation du scheduler d'envoi automatique"); // Exécuter immédiatement au démarrage processEnvoisAutomatiques(); // Puis exécuter tous les jours à 9h00 const interval = setInterval( () => { const now = new Date(); if (now.getHours() === 9 && now.getMinutes() === 0) { processEnvoisAutomatiques(); } }, 60 * 1000 // Vérifier toutes les minutes ); console.log("[Questionnaires] Scheduler initialisé - vérification quotidienne à 9h00"); return interval; }