Files
Yaojia Wang 2466cd4020 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>
2025-11-04 23:14:41 +01:00

73 lines
2.4 KiB
C#

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);
}
}