249 lines
7.7 KiB
TypeScript
249 lines
7.7 KiB
TypeScript
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: `
|
|
<h2>Bonjour ${apprenantPrenom} ${apprenantNom},</h2>
|
|
|
|
<p>Nous vous remercions d'avoir participé à la formation <strong>${sequenceNom}</strong>.</p>
|
|
|
|
<p>Afin d'améliorer continuellement la qualité de nos formations, nous vous invitons à répondre à ce questionnaire :</p>
|
|
|
|
<p style="margin: 30px 0;">
|
|
<a href="${lienQuestionnaire}"
|
|
style="background-color: #2563eb; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; display: inline-block;">
|
|
Répondre au questionnaire
|
|
</a>
|
|
</p>
|
|
|
|
<p>Ce questionnaire ne vous prendra que quelques minutes.</p>
|
|
|
|
<p>Merci pour votre participation !</p>
|
|
|
|
<p style="margin-top: 30px; font-size: 12px; color: #666;">
|
|
Si le bouton ne fonctionne pas, copiez ce lien dans votre navigateur :<br>
|
|
<a href="${lienQuestionnaire}">${lienQuestionnaire}</a>
|
|
</p>
|
|
`,
|
|
});
|
|
|
|
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;
|
|
}
|