feat: DB-backed localized templates + language-aware emails
- New Apis/myai-models project: MyAiDbContext (schema myAi), TemplateEntity, ITemplateService, DbTemplateService with 10-min in-memory cache - Seeds EN+RO variants for all user-facing templates (match email, job search results email, HTML status pages, AI system prompt) - Match result email now sent in user's UI language (en/ro) - Job search results email now respects session language - Language propagates: MatchJobRequest -> token -> session -> email - Add Language column to JobSearchTokens and JobSearchSessions (default 'en') - All three Dockerfiles updated to include myai-models in build context Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,6 @@ namespace Api.Services.Contracts;
|
||||
|
||||
public interface IJobTokenService
|
||||
{
|
||||
Task<string> CreateTokenAsync(string cvDocumentId, string email, CancellationToken ct);
|
||||
Task<string> CreateTokenAsync(string cvDocumentId, string email, string language, CancellationToken ct);
|
||||
Task<string> TriggerStartAsync(string tokenId, CancellationToken ct);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using CvMatcher.Models.Responses;
|
||||
using CvMatcher.Models.Settings;
|
||||
using Api.Services.Contracts;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MyAi.Models.Services;
|
||||
|
||||
namespace Api.Services;
|
||||
|
||||
@@ -17,19 +18,22 @@ public sealed class CvMatcherService : ICvMatcherService
|
||||
private readonly IMatcherAiClient _ai;
|
||||
private readonly IMatcherRepository _repository;
|
||||
private readonly MatcherSettings _settings;
|
||||
private readonly ITemplateService _templates;
|
||||
|
||||
public CvMatcherService(
|
||||
IRagApiClient rag,
|
||||
IJobTextExtractor jobTextExtractor,
|
||||
IMatcherAiClient ai,
|
||||
IMatcherRepository repository,
|
||||
IOptions<MatcherSettings> options)
|
||||
IOptions<MatcherSettings> options,
|
||||
ITemplateService templates)
|
||||
{
|
||||
_rag = rag;
|
||||
_jobTextExtractor = jobTextExtractor;
|
||||
_ai = ai;
|
||||
_repository = repository;
|
||||
_settings = options.Value;
|
||||
_templates = templates;
|
||||
}
|
||||
|
||||
public async Task<CvUploadResponse> UploadCvAsync(IFormFile file, CancellationToken ct)
|
||||
@@ -111,12 +115,8 @@ public sealed class CvMatcherService : ICvMatcherService
|
||||
var evidence = evidenceChunks.Count > 0 ? string.Join("\n\n", evidenceChunks.Take(4)) : Limit(job.Text, 4000);
|
||||
var languageName = LanguageName(language);
|
||||
|
||||
var systemPrompt = $$"""
|
||||
You are a strict CV-to-job matching engine. Return JSON only. Score realistically from 0 to 100.
|
||||
Penalize missing required skills. Do not invent experience. Use concise business language.
|
||||
Respond entirely in {{languageName}} — all text fields in the JSON must be in {{languageName}}.
|
||||
JSON shape: {"score":number,"summary":"...","strengths":["..."],"gaps":["..."],"recommendations":["..."],"evidence":["..."]}
|
||||
""";
|
||||
var systemPrompt = _templates.Render("ai.cv-match.system-prompt", "*",
|
||||
("languageName", languageName));
|
||||
|
||||
var userPrompt = $"""
|
||||
CV:
|
||||
|
||||
@@ -30,13 +30,14 @@ public sealed class JobTokenService : IJobTokenService
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<string> CreateTokenAsync(string cvDocumentId, string email, CancellationToken ct)
|
||||
public async Task<string> CreateTokenAsync(string cvDocumentId, string email, string language, CancellationToken ct)
|
||||
{
|
||||
var token = new JobSearchTokenEntity
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
CvDocumentId = cvDocumentId,
|
||||
Email = email,
|
||||
Language = language,
|
||||
ExpiresAt = DateTime.UtcNow.AddDays(_settings.TokenExpiryDays),
|
||||
Used = false,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
@@ -71,6 +72,7 @@ public sealed class JobTokenService : IJobTokenService
|
||||
TokenId = token.Id,
|
||||
CvDocumentId = token.CvDocumentId,
|
||||
Email = token.Email,
|
||||
Language = token.Language,
|
||||
Status = JobSearchStatus.Pending,
|
||||
Keywords = keywords,
|
||||
ProviderConfigJson = providerConfigJson,
|
||||
|
||||
Reference in New Issue
Block a user