FEAT-14a: Charakterbogen on Gerbil (traits + note) + sale-ad request extension

- Gerbil.CharacterTraits (List<string>, JSON text column, opaque labels) + CharacterNote
  (string?). On GerbilDto + Input; round-trips on GET/POST/PUT. Additive migration.
- SaleAdAnimal gains Traits (German labels) + CharacterNote; prompt builder emits
  "Charakter: …" + "Charakter-Notiz: …" lines for the AI Verkaufstext.
- Tests: traits/note round-trip (POST->GET via SQLite host), empty-default, prompt inclusion.
This commit is contained in:
2026-06-06 09:10:30 +02:00
parent f858a02f50
commit 3afb04f646
10 changed files with 1203 additions and 4 deletions

View File

@@ -49,6 +49,19 @@ public class ApplicationContext : DbContext
e.Property(g => g.Gender).HasConversion<string>();
e.Property(g => g.Status).HasConversion<string>();
// FEAT-14: character traits stored as a JSON text column (works on both
// Npgsql and the SQLite test host; opaque labels, no backend vocabulary).
var traitsConverter = new Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter<List<string>, string>(
v => System.Text.Json.JsonSerializer.Serialize(v, (System.Text.Json.JsonSerializerOptions?)null),
v => string.IsNullOrEmpty(v)
? new List<string>()
: System.Text.Json.JsonSerializer.Deserialize<List<string>>(v, (System.Text.Json.JsonSerializerOptions?)null) ?? new List<string>());
var traitsComparer = new Microsoft.EntityFrameworkCore.ChangeTracking.ValueComparer<List<string>>(
(a, b) => (a ?? new List<string>()).SequenceEqual(b ?? new List<string>()),
v => v == null ? 0 : v.Aggregate(0, (h, s) => HashCode.Combine(h, s.GetHashCode())),
v => v.ToList());
e.Property(g => g.CharacterTraits).HasConversion(traitsConverter, traitsComparer);
e.HasOne(g => g.Litter).WithMany()
.HasForeignKey(g => g.LitterId).OnDelete(DeleteBehavior.SetNull);
e.HasOne(g => g.OriginContact).WithMany()