Ingestion et indexation
- IIngestionService/IngestionService : chargement des collections filles par
sous-type, un jeu de morceaux par langue, ChunkIndex continu.
- SectionIndexingInterceptor retenu comme unique déclencheur : les 5
sous-contrôleurs totalisaient 30 SaveChanges et 0 Enqueue, donc ajouter des
points d'intérêt à une carte ne réindexait rien.
- HTML retiré avant l'embedding et lignes trop longues recoupées : sans cela un
article dépassait l'entrée max du modèle et emportait son lot de 50 morceaux.
- Gabarits de LanguageInit filtrés, DistinctBy(Text) avant le Take : ils
occupaient les cinq premiers résultats d'une recherche en néerlandais.
Endpoints du guide IA
- GET /api/Ai/knowledge/{id} : agrégats sur ContentEmbedding, donc sur ce qui
est réellement indexé — compter les sections publiées serait plus flatteur et faux.
- GET /api/Ai/insights/{id} : miroir de GuideIaInsights côté manager-app, c'est
l'écran qui a fixé la forme pour que le job de thèmes la remplisse.
RGPD
- VisitorQuestion journalisée dans AiController.Chat. HasAnswer se déduit des
sources du retrieval, pas du texte : un repli poli ressemble à une réponse.
L'écriture n'échoue jamais la réponse au visiteur.
- VisitorQuestionPurgeService, 90 jours, actif sans condition de configuration :
une durée écrite dans les CGU n'est pas un réglage commercial.
Corrections
- Updateinstance ne recopiait pas les quotas du nouveau plan.
- CheckQuota ne bloquait ni ne comptait à quota 0 — IA gratuite non comptée.
- StoragePath et SizeBytes renseignés à Create, types URL exclus.
dotnet build 0 erreur, dotnet test 130/130.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
134 lines
5.2 KiB
C#
134 lines
5.2 KiB
C#
using ManagerService.Data;
|
||
using ManagerService.DTOs;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel.DataAnnotations.Schema;
|
||
using System.ComponentModel.DataAnnotations;
|
||
using System.Linq;
|
||
using Microsoft.EntityFrameworkCore;
|
||
|
||
namespace ManagerService.Data.SubSection
|
||
{
|
||
[Index(nameof(InstanceId))]
|
||
public class GuidedPath : IAuditableEntity // Parcours
|
||
{
|
||
[Key]
|
||
public string Id { get; set; }
|
||
|
||
[Required]
|
||
public string InstanceId { get; set; }
|
||
|
||
public DateTime DateCreation { get; set; }
|
||
|
||
public DateTime DateUpdate { get; set; }
|
||
|
||
[Required]
|
||
[Column(TypeName = "jsonb")]
|
||
public List<TranslationDTO> Title { get; set; }
|
||
|
||
[Column(TypeName = "jsonb")]
|
||
public List<TranslationDTO> Description { get; set; }
|
||
|
||
// Lié à un événement (optionnel)
|
||
public string? SectionEventId { get; set; }
|
||
[ForeignKey("SectionEventId")]
|
||
public SectionEvent? SectionEvent { get; set; }
|
||
|
||
// Lié à une section parcours (FK principale)
|
||
public string? SectionParcoursId { get; set; }
|
||
[ForeignKey("SectionParcoursId")]
|
||
public SectionParcours? SectionParcours { get; set; }
|
||
|
||
// Type de parcours
|
||
public bool IsLinear { get; set; } = true; // Avancer dans l’ordre - Ordre obligatoire
|
||
public bool RequireSuccessToAdvance { get; set; } = false; // Par exemple: résoudre une énigme
|
||
public bool HideNextStepsUntilComplete { get; set; } = false; // Étapes cachées tant que non terminées
|
||
|
||
public int? EstimatedDurationMinutes { get; set; }
|
||
|
||
public string? ImageResourceId { get; set; }
|
||
|
||
[ForeignKey("ImageResourceId")]
|
||
public Resource? ImageResource { get; set; }
|
||
|
||
public int Order { get; set; }
|
||
|
||
public bool IsGameMode { get; set; } = false;
|
||
|
||
[Column(TypeName = "jsonb")]
|
||
public List<TranslationAndResourceDTO> GameMessageDebut { get; set; } = new();
|
||
|
||
[Column(TypeName = "jsonb")]
|
||
public List<TranslationAndResourceDTO> GameMessageFin { get; set; } = new();
|
||
|
||
public List<GuidedStep> Steps { get; set; } = new();
|
||
|
||
public string GetEmbeddableText(string language) =>
|
||
SectionText.JoinText(new[]
|
||
{
|
||
SectionText.Translate(Title, language),
|
||
SectionText.Translate(Description, language),
|
||
SectionText.Translate(GameMessageDebut, language),
|
||
SectionText.Translate(GameMessageFin, language)
|
||
}
|
||
.Concat((Steps ?? new List<GuidedStep>())
|
||
.OrderBy(s => s.Order)
|
||
.Select(s => s.GetEmbeddableText(language)))
|
||
.ToArray());
|
||
|
||
public IEnumerable<string> GetReferencedResourceIds(string language = null) =>
|
||
SectionText.ResourceId(ImageResourceId)
|
||
.Concat(SectionText.ResourceIds(GameMessageDebut, language))
|
||
.Concat(SectionText.ResourceIds(GameMessageFin, language))
|
||
.Concat((Steps ?? new List<GuidedStep>())
|
||
.SelectMany(s => s.GetReferencedResourceIds(language)));
|
||
|
||
public GuidedPathDTO ToDTO()
|
||
{
|
||
return new GuidedPathDTO
|
||
{
|
||
id = Id,
|
||
instanceId = InstanceId,
|
||
title = Title,
|
||
description = Description,
|
||
sectionEventId = SectionEventId,
|
||
sectionParcoursId = SectionParcoursId,
|
||
isLinear = IsLinear,
|
||
requireSuccessToAdvance = RequireSuccessToAdvance,
|
||
hideNextStepsUntilComplete = HideNextStepsUntilComplete,
|
||
estimatedDurationMinutes = EstimatedDurationMinutes,
|
||
imageResourceId = ImageResourceId,
|
||
imageUrl = ImageResource?.Url,
|
||
order = Order,
|
||
isGameMode = IsGameMode,
|
||
gameMessageDebut = GameMessageDebut,
|
||
gameMessageFin = GameMessageFin,
|
||
steps = Steps?.OrderBy(s => s.Order).Select(s => s.ToDTO()).ToList()
|
||
};
|
||
}
|
||
|
||
public GuidedPath FromDTO(GuidedPathDTO dto)
|
||
{
|
||
if (dto == null) return null;
|
||
|
||
InstanceId = dto.instanceId;
|
||
Title = dto.title ?? new List<TranslationDTO>();
|
||
Description = dto.description ?? new List<TranslationDTO>();
|
||
SectionEventId = dto.sectionEventId;
|
||
SectionParcoursId = dto.sectionParcoursId;
|
||
IsLinear = dto.isLinear;
|
||
RequireSuccessToAdvance = dto.requireSuccessToAdvance;
|
||
HideNextStepsUntilComplete = dto.hideNextStepsUntilComplete;
|
||
EstimatedDurationMinutes = dto.estimatedDurationMinutes;
|
||
ImageResourceId = dto.imageResourceId;
|
||
Order = dto.order.GetValueOrDefault();
|
||
IsGameMode = dto.isGameMode;
|
||
GameMessageDebut = dto.gameMessageDebut ?? new List<TranslationAndResourceDTO>();
|
||
GameMessageFin = dto.gameMessageFin ?? new List<TranslationAndResourceDTO>();
|
||
//Steps = dto.Steps?.Select(s => s.FromDTO()).ToList() ?? new List<GuidedStep>() // Other method
|
||
return this;
|
||
}
|
||
|
||
}
|
||
}
|