- Renamed all directories (TrueCV.* -> RealCV.*) - Renamed all project files (.csproj) - Renamed solution file (TrueCV.sln -> RealCV.sln) - Updated all namespaces in C# and Razor files - Updated project references - Updated CSS variable names 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
199 lines
6.4 KiB
C#
199 lines
6.4 KiB
C#
using System.Text.Json;
|
|
using Hangfire;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using RealCV.Application.DTOs;
|
|
using RealCV.Application.Helpers;
|
|
using RealCV.Application.Interfaces;
|
|
using RealCV.Application.Models;
|
|
using RealCV.Domain.Entities;
|
|
using RealCV.Domain.Enums;
|
|
using RealCV.Infrastructure.Data;
|
|
using RealCV.Infrastructure.Jobs;
|
|
|
|
namespace RealCV.Infrastructure.Services;
|
|
|
|
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;
|
|
}
|
|
|
|
public async Task<Guid> CreateCheckAsync(Guid userId, Stream file, string fileName)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(file);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(fileName);
|
|
|
|
_logger.LogDebug("Creating CV check for user {UserId}, file: {FileName}", userId, fileName);
|
|
|
|
// Upload file to blob storage
|
|
var blobUrl = await _fileStorageService.UploadAsync(file, fileName);
|
|
|
|
_logger.LogDebug("File uploaded to: {BlobUrl}", blobUrl);
|
|
|
|
// Create CV check record
|
|
var cvCheck = new CVCheck
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
UserId = userId,
|
|
OriginalFileName = fileName,
|
|
BlobUrl = blobUrl,
|
|
Status = CheckStatus.Pending
|
|
};
|
|
|
|
_dbContext.CVChecks.Add(cvCheck);
|
|
await _dbContext.SaveChangesAsync();
|
|
|
|
_logger.LogDebug("CV check record created with ID: {CheckId}", cvCheck.Id);
|
|
|
|
// Queue background job for processing
|
|
_backgroundJobClient.Enqueue<ProcessCVCheckJob>(job => job.ExecuteAsync(cvCheck.Id, CancellationToken.None));
|
|
|
|
_logger.LogInformation(
|
|
"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;
|
|
}
|
|
|
|
public async Task<CVCheckDto?> GetCheckAsync(Guid id)
|
|
{
|
|
_logger.LogDebug("Retrieving CV check: {CheckId}", id);
|
|
|
|
var cvCheck = await _dbContext.CVChecks
|
|
.AsNoTracking()
|
|
.FirstOrDefaultAsync(c => c.Id == id);
|
|
|
|
if (cvCheck is null)
|
|
{
|
|
_logger.LogDebug("CV check not found: {CheckId}", id);
|
|
return null;
|
|
}
|
|
|
|
return MapToDto(cvCheck);
|
|
}
|
|
|
|
public async Task<List<CVCheckDto>> GetUserChecksAsync(Guid userId)
|
|
{
|
|
_logger.LogDebug("Retrieving CV checks for user: {UserId}", userId);
|
|
|
|
var checks = await _dbContext.CVChecks
|
|
.AsNoTracking()
|
|
.Where(c => c.UserId == userId)
|
|
.OrderByDescending(c => c.CreatedAt)
|
|
.ToListAsync();
|
|
|
|
_logger.LogDebug("Found {Count} CV checks for user {UserId}", checks.Count, userId);
|
|
|
|
return checks.Select(MapToDto).ToList();
|
|
}
|
|
|
|
public async Task<CVCheckDto?> GetCheckForUserAsync(Guid id, Guid userId)
|
|
{
|
|
_logger.LogDebug("Retrieving CV check {CheckId} for user {UserId}", id, userId);
|
|
|
|
var cvCheck = await _dbContext.CVChecks
|
|
.AsNoTracking()
|
|
.FirstOrDefaultAsync(c => c.Id == id && c.UserId == userId);
|
|
|
|
if (cvCheck is null)
|
|
{
|
|
_logger.LogDebug("CV check not found: {CheckId} for user {UserId}", id, userId);
|
|
return null;
|
|
}
|
|
|
|
return MapToDto(cvCheck);
|
|
}
|
|
|
|
public async Task<VeracityReport?> GetReportAsync(Guid checkId, Guid userId)
|
|
{
|
|
_logger.LogDebug("Retrieving report for CV check {CheckId}, user {UserId}", checkId, userId);
|
|
|
|
var cvCheck = await _dbContext.CVChecks
|
|
.AsNoTracking()
|
|
.FirstOrDefaultAsync(c => c.Id == checkId && c.UserId == userId);
|
|
|
|
if (cvCheck is null)
|
|
{
|
|
_logger.LogWarning("CV check not found: {CheckId} for user {UserId}", checkId, userId);
|
|
return null;
|
|
}
|
|
|
|
if (cvCheck.Status != CheckStatus.Completed || string.IsNullOrEmpty(cvCheck.ReportJson))
|
|
{
|
|
_logger.LogDebug("CV check {CheckId} not completed or has no report", checkId);
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
var report = JsonSerializer.Deserialize<VeracityReport>(cvCheck.ReportJson, JsonDefaults.CamelCase);
|
|
return report;
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to deserialize report JSON for check {CheckId}", checkId);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
Id = cvCheck.Id,
|
|
OriginalFileName = cvCheck.OriginalFileName,
|
|
Status = cvCheck.Status.ToString(),
|
|
VeracityScore = cvCheck.VeracityScore,
|
|
ProcessingStage = cvCheck.ProcessingStage,
|
|
CreatedAt = cvCheck.CreatedAt,
|
|
CompletedAt = cvCheck.CompletedAt
|
|
};
|
|
}
|
|
}
|