Initial commit: TrueCV CV verification platform

Clean architecture solution with:
- Domain: Entities (User, CVCheck, CVFlag, CompanyCache) and Enums
- Application: Service interfaces, DTOs, and models
- Infrastructure: EF Core, Identity, Hangfire, external API clients, services
- Web: Blazor Server UI with pages and components

Features:
- CV upload and parsing (PDF/DOCX) using Claude API
- Employment verification against Companies House API
- Timeline analysis for gaps and overlaps
- Veracity scoring algorithm
- Background job processing with Hangfire
- Azure Blob Storage for file storage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-18 19:20:50 +01:00
commit 6d514e01b2
70 changed files with 5996 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
namespace TrueCV.Application.DTOs;
public sealed record CVCheckDto
{
public required Guid Id { get; init; }
public required string OriginalFileName { get; init; }
public required string Status { get; init; }
public int? VeracityScore { get; init; }
public required DateTime CreatedAt { get; init; }
public DateTime? CompletedAt { get; init; }
}

View File

@@ -0,0 +1,10 @@
namespace TrueCV.Application.DTOs;
public sealed record CompanySearchResult
{
public required string CompanyNumber { get; init; }
public required string CompanyName { get; init; }
public required string CompanyStatus { get; init; }
public DateOnly? IncorporationDate { get; init; }
public string? AddressSnippet { get; init; }
}

View File

@@ -0,0 +1,13 @@
using TrueCV.Application.DTOs;
using TrueCV.Application.Models;
namespace TrueCV.Application.Interfaces;
public interface ICVCheckService
{
Task<Guid> CreateCheckAsync(Guid userId, Stream file, string fileName);
Task<CVCheckDto?> GetCheckAsync(Guid id);
Task<CVCheckDto?> GetCheckForUserAsync(Guid id, Guid userId);
Task<List<CVCheckDto>> GetUserChecksAsync(Guid userId);
Task<VeracityReport?> GetReportAsync(Guid checkId, Guid userId);
}

View File

@@ -0,0 +1,8 @@
using TrueCV.Application.Models;
namespace TrueCV.Application.Interfaces;
public interface ICVParserService
{
Task<CVData> ParseAsync(Stream fileStream, string fileName);
}

View File

@@ -0,0 +1,10 @@
using TrueCV.Application.DTOs;
using TrueCV.Application.Models;
namespace TrueCV.Application.Interfaces;
public interface ICompanyVerifierService
{
Task<CompanyVerificationResult> VerifyCompanyAsync(string companyName, DateOnly? startDate, DateOnly? endDate);
Task<List<CompanySearchResult>> SearchCompaniesAsync(string query);
}

View File

@@ -0,0 +1,8 @@
namespace TrueCV.Application.Interfaces;
public interface IFileStorageService
{
Task<string> UploadAsync(Stream fileStream, string fileName);
Task<Stream> DownloadAsync(string blobUrl);
Task DeleteAsync(string blobUrl);
}

View File

@@ -0,0 +1,8 @@
using TrueCV.Application.Models;
namespace TrueCV.Application.Interfaces;
public interface ITimelineAnalyserService
{
TimelineAnalysisResult Analyse(List<EmploymentEntry> employmentHistory);
}

View File

@@ -0,0 +1,11 @@
namespace TrueCV.Application.Models;
public sealed record CVData
{
public required string FullName { get; init; }
public string? Email { get; init; }
public string? Phone { get; init; }
public List<EmploymentEntry> Employment { get; init; } = [];
public List<EducationEntry> Education { get; init; } = [];
public List<string> Skills { get; init; } = [];
}

View File

@@ -0,0 +1,13 @@
namespace TrueCV.Application.Models;
public sealed record CompanyVerificationResult
{
public required string ClaimedCompany { get; init; }
public string? MatchedCompanyName { get; init; }
public string? MatchedCompanyNumber { get; init; }
public required int MatchScore { get; init; }
public required bool IsVerified { get; init; }
public string? VerificationNotes { get; init; }
public DateOnly? ClaimedStartDate { get; init; }
public DateOnly? ClaimedEndDate { get; init; }
}

View File

@@ -0,0 +1,11 @@
namespace TrueCV.Application.Models;
public sealed record EducationEntry
{
public required string Institution { get; init; }
public string? Qualification { get; init; }
public string? Subject { get; init; }
public string? Grade { get; init; }
public DateOnly? StartDate { get; init; }
public DateOnly? EndDate { get; init; }
}

View File

@@ -0,0 +1,12 @@
namespace TrueCV.Application.Models;
public sealed record EmploymentEntry
{
public required string CompanyName { get; init; }
public required string JobTitle { get; init; }
public string? Location { get; init; }
public DateOnly? StartDate { get; init; }
public DateOnly? EndDate { get; init; }
public bool IsCurrent { get; init; }
public string? Description { get; init; }
}

View File

@@ -0,0 +1,10 @@
namespace TrueCV.Application.Models;
public sealed record FlagResult
{
public required string Category { get; init; }
public required string Severity { get; init; }
public required string Title { get; init; }
public required string Description { get; init; }
public required int ScoreImpact { get; init; }
}

View File

@@ -0,0 +1,9 @@
namespace TrueCV.Application.Models;
public sealed record TimelineAnalysisResult
{
public required int TotalGapMonths { get; init; }
public required int TotalOverlapMonths { get; init; }
public List<TimelineGap> Gaps { get; init; } = [];
public List<TimelineOverlap> Overlaps { get; init; } = [];
}

View File

@@ -0,0 +1,8 @@
namespace TrueCV.Application.Models;
public sealed record TimelineGap
{
public required DateOnly StartDate { get; init; }
public required DateOnly EndDate { get; init; }
public required int Months { get; init; }
}

View File

@@ -0,0 +1,10 @@
namespace TrueCV.Application.Models;
public sealed record TimelineOverlap
{
public required string Company1 { get; init; }
public required string Company2 { get; init; }
public required DateOnly OverlapStart { get; init; }
public required DateOnly OverlapEnd { get; init; }
public required int Months { get; init; }
}

View File

@@ -0,0 +1,11 @@
namespace TrueCV.Application.Models;
public sealed record VeracityReport
{
public required int OverallScore { get; init; }
public required string ScoreLabel { get; init; }
public List<CompanyVerificationResult> EmploymentVerifications { get; init; } = [];
public required TimelineAnalysisResult TimelineAnalysis { get; init; }
public List<FlagResult> Flags { get; init; } = [];
public required DateTime GeneratedAt { get; init; }
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\TrueCV.Domain\TrueCV.Domain.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>