Files
Yaojia Wang de6af53a77 feat(backend): Add AuditLog database schema and migration
Implement AuditLog entity and EF Core configuration for Sprint 2 Story 1 Task 1.

Changes:
- Created AuditLog entity with multi-tenant support
- Added EF Core configuration with JSONB columns for PostgreSQL
- Created composite indexes for query optimization
- Generated database migration (20251104220842_AddAuditLogTable)
- Updated PMDbContext with AuditLog DbSet and query filter
- Updated task status to in_progress in sprint plan

Technical Details:
- PostgreSQL JSONB type for OldValues/NewValues (flexible schema)
- Composite index on (TenantId, EntityType, EntityId) for entity history queries
- Timestamp index (DESC) for recent logs queries
- UserId index for user activity tracking
- Multi-tenant query filter applied to AuditLog

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 23:10:12 +01:00

81 lines
2.8 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using ColaFlow.Modules.ProjectManagement.Domain.Entities;
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence.Configurations;
/// <summary>
/// Entity configuration for AuditLog
/// Configures multi-tenant isolation, JSONB columns, and composite indexes
/// </summary>
public class AuditLogConfiguration : IEntityTypeConfiguration<AuditLog>
{
public void Configure(EntityTypeBuilder<AuditLog> builder)
{
builder.ToTable("AuditLogs");
// Primary key
builder.HasKey(a => a.Id);
builder.Property(a => a.Id)
.IsRequired()
.ValueGeneratedNever();
// TenantId conversion (StronglyTypedId to Guid)
builder.Property(a => a.TenantId)
.HasConversion(
id => id.Value,
value => TenantId.From(value))
.IsRequired();
// EntityType - the type name of the entity being audited
builder.Property(a => a.EntityType)
.IsRequired()
.HasMaxLength(100);
// EntityId - the ID of the entity being audited
builder.Property(a => a.EntityId)
.IsRequired();
// Action - Create, Update, Delete
builder.Property(a => a.Action)
.IsRequired()
.HasMaxLength(20);
// UserId conversion (nullable StronglyTypedId to Guid)
builder.Property(a => a.UserId)
.HasConversion(
id => id != null ? id.Value : (Guid?)null,
value => value.HasValue ? UserId.From(value.Value) : null);
// Timestamp with UTC
builder.Property(a => a.Timestamp)
.IsRequired();
// OldValues as JSONB (PostgreSQL-specific)
builder.Property(a => a.OldValues)
.HasColumnType("jsonb");
// NewValues as JSONB (PostgreSQL-specific)
builder.Property(a => a.NewValues)
.HasColumnType("jsonb");
// Composite index for efficient entity history queries
// Query pattern: Get all audit logs for a specific entity within a tenant
builder.HasIndex(a => new { a.TenantId, a.EntityType, a.EntityId })
.HasDatabaseName("IX_AuditLogs_TenantId_EntityType_EntityId");
// Index for recent logs queries (DESC order for performance)
// Query pattern: Get most recent audit logs across all entities
builder.HasIndex(a => a.Timestamp)
.HasDatabaseName("IX_AuditLogs_Timestamp")
.IsDescending();
// Index for user activity tracking
// Query pattern: Get all actions performed by a specific user
builder.HasIndex(a => a.UserId)
.HasDatabaseName("IX_AuditLogs_UserId");
}
}