Add audit logging, processing stages, delete functionality, and bug fixes

- Add audit logging system for tracking CV uploads, processing, deletion,
  report views, and PDF exports for billing/reference purposes
- Add processing stage display on dashboard instead of generic "Processing"
- Add delete button for CV checks on dashboard
- Fix duplicate primary key error in CompanyCache (race condition)
- Fix DbContext concurrency in Dashboard (concurrent delete/load operations)
- Fix ProcessCVCheckJob to handle deleted records gracefully
- Fix duplicate flags in verification report by deduplicating on Title+Description
- Remove internal cache notes from verification results
- Add EF migrations for ProcessingStage and AuditLog table

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 20:58:12 +01:00
parent 652aa2e612
commit 0eee5473e4
21 changed files with 1559 additions and 123 deletions

View File

@@ -18,17 +18,20 @@ public sealed class CVCheckService : ICVCheckService
private readonly ApplicationDbContext _dbContext;
private readonly IFileStorageService _fileStorageService;
private readonly IBackgroundJobClient _backgroundJobClient;
private readonly IAuditService _auditService;
private readonly ILogger<CVCheckService> _logger;
public CVCheckService(
ApplicationDbContext dbContext,
IFileStorageService fileStorageService,
IBackgroundJobClient backgroundJobClient,
IAuditService auditService,
ILogger<CVCheckService> logger)
{
_dbContext = dbContext;
_fileStorageService = fileStorageService;
_backgroundJobClient = backgroundJobClient;
_auditService = auditService;
_logger = logger;
}
@@ -66,6 +69,8 @@ public sealed class CVCheckService : ICVCheckService
"CV check {CheckId} created for user {UserId}, processing queued",
cvCheck.Id, userId);
await _auditService.LogAsync(userId, AuditActions.CVUploaded, "CVCheck", cvCheck.Id, $"File: {fileName}");
return cvCheck.Id;
}
@@ -150,6 +155,33 @@ public sealed class CVCheckService : ICVCheckService
}
}
public async Task<bool> DeleteCheckAsync(Guid checkId, Guid userId)
{
_logger.LogDebug("Deleting CV check {CheckId} for user {UserId}", checkId, userId);
var cvCheck = await _dbContext.CVChecks
.Include(c => c.Flags)
.FirstOrDefaultAsync(c => c.Id == checkId && c.UserId == userId);
if (cvCheck is null)
{
_logger.LogWarning("CV check {CheckId} not found for user {UserId}", checkId, userId);
return false;
}
var fileName = cvCheck.OriginalFileName;
_dbContext.CVFlags.RemoveRange(cvCheck.Flags);
_dbContext.CVChecks.Remove(cvCheck);
await _dbContext.SaveChangesAsync();
_logger.LogInformation("Deleted CV check {CheckId} for user {UserId}", checkId, userId);
await _auditService.LogAsync(userId, AuditActions.CVDeleted, "CVCheck", checkId, $"File: {fileName}");
return true;
}
private static CVCheckDto MapToDto(CVCheck cvCheck)
{
return new CVCheckDto
@@ -158,6 +190,7 @@ public sealed class CVCheckService : ICVCheckService
OriginalFileName = cvCheck.OriginalFileName,
Status = cvCheck.Status.ToString(),
VeracityScore = cvCheck.VeracityScore,
ProcessingStage = cvCheck.ProcessingStage,
CreatedAt = cvCheck.CreatedAt,
CompletedAt = cvCheck.CompletedAt
};