using System.Text.Json; using Microsoft.EntityFrameworkCore; public class IdempotencyService : IIdempotencyService { private readonly AppDbContext _db; private static readonly TimeSpan DefaultTtl = TimeSpan.FromHours(24); public IdempotencyService(AppDbContext db) { _db = db; } public async Task GetExistingAsync(string idempotencyKey, string operationName) { var record = await _db.IdempotencyRecords .FirstOrDefaultAsync(r => r.IdempotencyKey == idempotencyKey && r.OperationName == operationName && r.ExpiresAt > DateTimeOffset.UtcNow); return record; } public async Task SaveAsync(string idempotencyKey, string operationName, Guid resourceId, int httpStatusCode, object responseBody, TimeSpan? ttl = null) { var effectiveTtl = ttl ?? DefaultTtl; var now = DateTimeOffset.UtcNow; var record = new IdempotencyRecord { Id = Guid.NewGuid(), IdempotencyKey = idempotencyKey, OperationName = operationName, ResourceId = resourceId, HttpStatusCode = httpStatusCode, ResponseBodyJson = JsonSerializer.Serialize(responseBody, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false }), CreatedAt = now, ExpiresAt = now.Add(effectiveTtl) }; _db.IdempotencyRecords.Add(record); // SaveChanges is called by the caller (within the same transaction) } }