#nullable enable
using Baya.Application.Contracts.Common;
using Baya.Domain.Common;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
namespace Baya.Infrastructure.Persistence.Interceptors;
///
/// Stamps audit fields on every save: CreatedAt/CreatedById on insert and
/// ModifiedAt/ModifiedById on update, sourcing time from
/// and the acting user from . Handlers never set these fields.
/// This is the extension point backend-phase-1 builds on to also write append-only audit-log rows.
///
public sealed class AuditFieldInterceptor(ICurrentUser currentUser, IDateTimeProvider dateTimeProvider)
: SaveChangesInterceptor
{
public override InterceptionResult SavingChanges(
DbContextEventData eventData,
InterceptionResult result)
{
Stamp(eventData.Context);
return base.SavingChanges(eventData, result);
}
public override ValueTask> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult result,
CancellationToken cancellationToken = default)
{
Stamp(eventData.Context);
return base.SavingChangesAsync(eventData, result, cancellationToken);
}
private void Stamp(DbContext? context)
{
if (context is null)
return;
var now = dateTimeProvider.UtcNow;
var userId = currentUser.UserId;
foreach (var entry in context.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedAt = now;
entry.Entity.ModifiedAt = now;
if (entry.Entity is IAuditableEntity addedAuditable)
{
addedAuditable.CreatedById = userId;
addedAuditable.ModifiedById = userId;
}
break;
case EntityState.Modified:
entry.Entity.ModifiedAt = now;
if (entry.Entity is IAuditableEntity modifiedAuditable)
modifiedAuditable.ModifiedById = userId;
break;
}
}
}
}