Checkpoint: Amélioration système de rappels - Historique et notifications d'échec

**Nouvelles fonctionnalités** :

1. **Historique des rappels envoyés**
   - Nouvelle table `logsRappels` pour tracer tous les emails de rappels
   - Enregistrement automatique de chaque envoi (succès ou échec)
   - Prévention des doublons : vérification avant envoi
   - Informations stockées : rappelId, sequenceId, apprenantId, email, type, statut, message d'erreur

2. **Notifications automatiques à l'administrateur**
   - Alerte par email en cas d'échec d'envoi de rappels
   - Détail des échecs : liste des emails et messages d'erreur
   - Résumé : nombre d'envois réussis vs échecs

3. **Procédure de test manuel** (backend uniquement)
   - Nouvelle procédure tRPC `rappels.testerRappel`
   - Permet de tester l'envoi d'un rappel J-7 ou J-1 pour une séquence
   - Envoie au premier apprenant inscrit uniquement

**Fichiers créés** :
- `server/rappelDb.ts` : Fonctions de gestion des logs de rappels
- `drizzle/schema.ts` : Ajout de la table `logsRappels`

**Fichiers modifiés** :
- `server/rappelScheduler.ts` : Intégration du logging et des notifications
- `server/routers.ts` : Ajout de la procédure `testerRappel`

**Bénéfices** :
-  Aucun doublon même en cas de redémarrage du serveur
-  Traçabilité complète de tous les rappels envoyés
-  Alertes automatiques en cas de problème d'envoi
-  Possibilité de tester les rappels avant envoi automatique
This commit is contained in:
Manus Sandbox
2025-12-08 03:01:50 -05:00
parent 4077c57c49
commit bb0ec9e3b0
9 changed files with 2069 additions and 1 deletions

View File

@@ -446,3 +446,33 @@ export const supportsFormation = mysqlTable("supportsFormation", {
export type SupportFormation = typeof supportsFormation.$inferSelect;
export type InsertSupportFormation = typeof supportsFormation.$inferInsert;
/**
* Table des logs des rappels envoyés
* Stocke l'historique de tous les rappels envoyés pour éviter les doublons
*/
export const logsRappels = mysqlTable("logsRappels", {
id: int("id").autoincrement().primaryKey(),
/** ID du rappel (règle de rappel) */
rappelId: int("rappelId").notNull(),
/** ID de la séquence concernée */
sequenceId: int("sequenceId").notNull(),
/** ID de l'apprenant destinataire */
apprenantId: int("apprenantId").notNull(),
/** Email du destinataire */
emailDestinataire: varchar("emailDestinataire", { length: 320 }).notNull(),
/** Type de rappel (rappel, rappelJ1) */
typeRappel: varchar("typeRappel", { length: 50 }).notNull(),
/** Date d'envoi du rappel */
dateEnvoi: timestamp("dateEnvoi").defaultNow().notNull(),
/** Statut de l'envoi (success, failed) */
statut: mysqlEnum("statut", ["success", "failed"]).notNull(),
/** Message d'erreur en cas d'échec */
messageErreur: text("messageErreur"),
/** Date de la première date de la séquence (pour référence) */
dateSequence: timestamp("dateSequence").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type LogRappel = typeof logsRappels.$inferSelect;
export type InsertLogRappel = typeof logsRappels.$inferInsert;