feat(backend): Add AuditLog repository interface and implementation
Implement repository pattern for AuditLog entity for Sprint 2 Story 1 Task 2. Changes: - Created IAuditLogRepository interface with 6 query methods - Implemented AuditLogRepository with efficient querying - Registered repository in DI container - All queries use AsNoTracking for read-only operations Query Methods: - GetByIdAsync: Get single audit log by ID - GetByEntityAsync: Get audit history for specific entity - GetByUserAsync: Get user activity with pagination - GetRecentAsync: Get recent audit logs - AddAsync: Add new audit log - GetCountAsync: Get total audit log count Performance: - All queries automatically filtered by TenantId (Global Query Filter) - Efficient use of composite indexes - AsNoTracking for read-only operations Testing: - All tests passing (192 domain + 113 identity + 8 arch + 32 app + 12 infra = 357 tests) - No compilation errors - Zero test failures 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,7 @@ public static class ModuleExtensions
|
||||
|
||||
// Register repositories
|
||||
services.AddScoped<IProjectRepository, ProjectRepository>();
|
||||
services.AddScoped<IAuditLogRepository, ColaFlow.Modules.ProjectManagement.Infrastructure.Repositories.AuditLogRepository>();
|
||||
services.AddScoped<IUnitOfWork, ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence.UnitOfWork>();
|
||||
|
||||
// Register services
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.Entities;
|
||||
|
||||
namespace ColaFlow.Modules.ProjectManagement.Domain.Repositories;
|
||||
|
||||
public interface IAuditLogRepository
|
||||
{
|
||||
Task<AuditLog?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyList<AuditLog>> GetByEntityAsync(
|
||||
string entityType,
|
||||
Guid entityId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyList<AuditLog>> GetByUserAsync(
|
||||
Guid userId,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 50,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyList<AuditLog>> GetRecentAsync(
|
||||
int count = 100,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task AddAsync(AuditLog auditLog, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<int> GetCountAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.Entities;
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.Repositories;
|
||||
using ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Repositories;
|
||||
|
||||
public class AuditLogRepository : IAuditLogRepository
|
||||
{
|
||||
private readonly PMDbContext _context;
|
||||
|
||||
public AuditLogRepository(PMDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<AuditLog?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.AuditLogs
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<AuditLog>> GetByEntityAsync(
|
||||
string entityType,
|
||||
Guid entityId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.AuditLogs
|
||||
.AsNoTracking()
|
||||
.Where(a => a.EntityType == entityType && a.EntityId == entityId)
|
||||
.OrderByDescending(a => a.Timestamp)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<AuditLog>> GetByUserAsync(
|
||||
Guid userId,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 50,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.AuditLogs
|
||||
.AsNoTracking()
|
||||
.Where(a => a.UserId == userId)
|
||||
.OrderByDescending(a => a.Timestamp)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<AuditLog>> GetRecentAsync(
|
||||
int count = 100,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.AuditLogs
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(a => a.Timestamp)
|
||||
.Take(count)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task AddAsync(AuditLog auditLog, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _context.AuditLogs.AddAsync(auditLog, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<int> GetCountAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.AuditLogs.CountAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
---
|
||||
task_id: sprint_2_story_1_task_2
|
||||
story: sprint_2_story_1
|
||||
status: not_started
|
||||
status: in_progress
|
||||
estimated_hours: 4
|
||||
created_date: 2025-11-05
|
||||
start_date: 2025-11-05
|
||||
assignee: Backend Team
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user