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:
2026-05-24 18:06:44 +03:00
parent 2cada13fe3
commit fc6fe7a78b
34 changed files with 927 additions and 98 deletions
@@ -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: