fix(backend): resolve TR json serialization, news concurrency and asset index locks

This commit is contained in:
2026-08-16 16:57:34 +02:00
parent d8ba28a810
commit aaef272f4e
5 changed files with 81 additions and 41 deletions
+45 -32
View File
@@ -211,52 +211,65 @@ public class NewsDbService : INewsDbService
/// <inheritdoc />
public async Task<NewsArticleEntity?> SaveArticleClassificationAsync(Guid id, N8nResponsePayload payload, List<MatchedAssetEntity> matchedAssets)
{
var article = await _context.NewsArticles
.FirstOrDefaultAsync(a => a.Id == id);
DateTime? finalPublishedAt = null;
if (DateTime.TryParse(payload.PublishedAt, out var publishedDate))
{
finalPublishedAt = publishedDate.Kind == DateTimeKind.Unspecified
? DateTime.SpecifyKind(publishedDate, DateTimeKind.Utc)
: publishedDate.ToUniversalTime();
}
if (article == null)
DateTime? finalScrapedAt = null;
if (DateTime.TryParse(payload.ScrapedAt, out var scrapedDate))
{
finalScrapedAt = scrapedDate.ToUniversalTime();
}
var rowsAffected = await _context.NewsArticles
.Where(a => a.Id == id)
.ExecuteUpdateAsync(s => s
.SetProperty(a => a.Title, payload.Title)
.SetProperty(a => a.Author, payload.Author)
.SetProperty(a => a.Summary, payload.Summary)
.SetProperty(a => a.ContentRaw, payload.ContentRaw)
.SetProperty(a => a.Language, payload.Language)
.SetProperty(a => a.Status, "Completed")
.SetProperty(a => a.PublishedAt, a => finalPublishedAt ?? a.PublishedAt)
.SetProperty(a => a.ScrapedAt, a => finalScrapedAt ?? a.ScrapedAt));
if (rowsAffected == 0)
{
await _finlyticLogger.LogWarningAsync(SettingKeys.NewsChannel, "[NewsChannel] Article with ID {Id} not found for classification update.", id);
return null;
}
article.Title = payload.Title;
article.Author = payload.Author;
article.Summary = payload.Summary;
article.ContentRaw = payload.ContentRaw;
article.Language = payload.Language;
article.Status = "Completed";
if (DateTime.TryParse(payload.PublishedAt, out var publishedDate))
{
article.PublishedAt = publishedDate.Kind == DateTimeKind.Unspecified
? DateTime.SpecifyKind(publishedDate, DateTimeKind.Utc)
: publishedDate.ToUniversalTime();
}
if (DateTime.TryParse(payload.ScrapedAt, out var scrapedDate))
{
article.ScrapedAt = scrapedDate.ToUniversalTime();
}
// Cleanly delete existing matched assets and insert new ones
await _context.MatchedAssets.Where(m => m.NewsArticleId == id).ExecuteDeleteAsync();
article.MatchedAssets = new List<MatchedAssetEntity>();
foreach (var asset in matchedAssets)
if (matchedAssets != null && matchedAssets.Count > 0)
{
if (asset.Id == Guid.Empty)
foreach (var asset in matchedAssets)
{
asset.Id = Guid.NewGuid();
if (asset.Id == Guid.Empty)
{
asset.Id = Guid.NewGuid();
}
asset.NewsArticleId = id;
}
_context.MatchedAssets.Add(asset);
article.MatchedAssets.Add(asset);
await _context.MatchedAssets.AddRangeAsync(matchedAssets);
await _context.SaveChangesAsync();
}
await _context.SaveChangesAsync();
await _finlyticLogger.LogInfoAsync(SettingKeys.NewsChannel, "[Lifecycle] Article {Id} successfully classified and marked 'Completed'. Title: '{Title}'", article.Id, article.Title);
_context.ChangeTracker.Clear();
return article;
var completedArticle = await _context.NewsArticles
.Include(a => a.MatchedAssets)
.AsNoTracking()
.FirstOrDefaultAsync(a => a.Id == id);
await _finlyticLogger.LogInfoAsync(SettingKeys.NewsChannel, "[Lifecycle] Article {Id} successfully classified and marked 'Completed'. Title: '{Title}'", id, payload.Title);
return completedArticle;
}
/// <inheritdoc />