Table QuestionThemeMonthly (instance, mois, thème, compteur). C'est elle qui rend tenable le §8.4 des CGU : le regroupement vivait dans ThemeId, colonne de la ligne VisitorQuestion, donc la purge du 90e jour l'emportait avec la question et le client perdait tout au 91e. Elle ne porte que des compteurs — aucune donnée personnelle, ce qui est précisément ce qui l'autorise à survivre. Insights lit désormais les thèmes dans cette table, pas dans les questions de la fenêtre. Liste fixe de 8 thèmes, pas de thèmes découverts par l'IA : des libellés régénérés à chaque passage donneraient « Horaires » en janvier et « Questions d'horaires » en février, deux lignes distinctes et une courbe qui ne veut rien dire — alors que la table existe pour porter cet historique. Le job tourne à 2 h, la purge à 3 h 30 : une question purgée avant d'avoir été classée ne compte dans aucun agrégat et rien ne peut la rattraper. Le plafond de 500 par passage ne perd rien, il retarde — les plus anciennes d'abord, un passage par jour, avertissement si le retard dépasse un passage. Les jetons ne sont pas décomptés du quota client : il n'a pas demandé ces appels. Un lot en échec n'est pas marqué « Autre » pour s'en débarrasser, ce serait une perte définitive maquillée en résultat ; et la relecture se fait par numéro, jamais par position, pour qu'une ligne manquante ne décale pas les suivantes. Instance.IsVisitorQuestionCollectionEnabled (défaut true) + garde dans Chat : le client est responsable de traitement, la collecte était inconditionnelle. Ajout d'une fabrique design-time : EF construisait tout l'hôte pour trouver le contexte, et l'hôte ouvre une connexion au démarrage — générer une migration exigeait donc une base joignable, impossible sur une machine sans Postgres ni Docker. dotnet test : 211 passés, 15 sautés, 0 échec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
41 lines
1.8 KiB
C#
41 lines
1.8 KiB
C#
using System;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace ManagerService.Data
|
|
{
|
|
/// <summary>
|
|
/// Compteur mensuel de questions par thème, pour une instance.
|
|
///
|
|
/// ⚠️ **C'est cette table qui rend tenable la promesse du §8.4 des CGU** — « les
|
|
/// regroupements par thème sont conservés au-delà, sous forme agrégée ». Sans elle, le
|
|
/// regroupement vit dans <see cref="VisitorQuestion.ThemeId"/>, donc la purge du 90ᵉ jour
|
|
/// l'emporte avec la question : le client perdrait tout son historique au 91ᵉ jour.
|
|
///
|
|
/// Elle ne porte **que des compteurs** : ni texte de question, ni identifiant de session,
|
|
/// ni langue. Aucune donnée personnelle, donc rien qui justifierait de la purger — c'est
|
|
/// précisément ce qui l'autorise à survivre aux questions dont elle est issue.
|
|
/// </summary>
|
|
[Index(nameof(InstanceId), nameof(Month))]
|
|
public class QuestionThemeMonthly
|
|
{
|
|
public long Id { get; set; }
|
|
|
|
public string InstanceId { get; set; }
|
|
|
|
/// <summary>Premier jour du mois concerné, en UTC. La granularité est le mois, pas le jour.</summary>
|
|
public DateTime Month { get; set; }
|
|
|
|
/// <summary>
|
|
/// Libellé du thème, pris dans <see cref="Services.QuestionThemes.All"/>.
|
|
///
|
|
/// ⚠️ Volontairement une liste fixe et non des thèmes découverts par l'IA : deux mois
|
|
/// ne se comparent que si leurs thèmes portent le même nom. Des libellés régénérés à
|
|
/// chaque passage produiraient « Horaires » en janvier et « Questions d'horaires » en
|
|
/// février — deux lignes distinctes, et une courbe qui ne veut rien dire.
|
|
/// </summary>
|
|
public string Theme { get; set; }
|
|
|
|
public int Count { get; set; }
|
|
}
|
|
}
|