Update release notes and deploy workspace
CI - Build & Verify / Build, Typecheck & Hygiene (push) Successful in 59s
CI - Build & Verify / Deploy to award.noveria.net (push) Failing after 53s

This commit is contained in:
AzuTear
2026-06-29 17:49:54 +02:00
parent c466348d18
commit 441ef2b850
196 changed files with 9279 additions and 39292 deletions
@@ -44,6 +44,13 @@ public static partial class AdminSeasonManagementEndpoints
string ChannelSlug,
string? ClipCompilationUrl);
private sealed record WinnerPublicationSnapshot(
int CategoryId,
int? StreamerIdentityId,
string DisplayName,
string ChannelSlug,
string? ClipCompilationUrl);
private static IResult? ValidateSeasonRequest(CreateSeasonRequest request)
{
if (request.Year < 2020 || request.Year > 2100)
@@ -108,11 +115,6 @@ public static partial class AdminSeasonManagementEndpoints
return null;
}
private static string NormalizeSeasonStreamUrl(string? showStreamUrl)
{
return SeasonMappings.NormalizeSeasonStreamUrl(showStreamUrl);
}
private static bool IsKnownSeasonPhase(string? currentPhase)
{
var value = currentPhase?.Trim().ToLowerInvariant() ?? string.Empty;
@@ -156,7 +158,17 @@ public static partial class AdminSeasonManagementEndpoints
var issueList = issues.ToArray();
return Results.BadRequest(new
{
message = $"Public-/Archiv-Readiness blockiert: {string.Join(" ", issueList)}",
message = $"Landingpage-Freigabe blockiert: {string.Join(" ", issueList)}",
issues = issueList,
});
}
private static IResult CreateWinnerPublicationError(IEnumerable<string> issues)
{
var issueList = issues.ToArray();
return Results.BadRequest(new
{
message = $"Gewinner-Freigabe blockiert: {string.Join(" ", issueList)}",
issues = issueList,
});
}
@@ -421,6 +433,87 @@ public static partial class AdminSeasonManagementEndpoints
return issues.ToArray();
}
private static async Task<string[]> BuildWinnerPublicationIssuesAsync(
AwardsDbContext db,
int seasonId,
CancellationToken cancellationToken)
{
var categoryIds = await db.Categories
.AsNoTracking()
.Where(item => item.SeasonId == seasonId)
.Select(item => item.Id)
.ToArrayAsync(cancellationToken);
var issues = new List<string>();
if (categoryIds.Length == 0)
{
issues.Add("Mindestens eine Kategorie ist erforderlich.");
return issues.ToArray();
}
var resultSnapshots = await db.Results
.AsNoTracking()
.Where(item => item.SeasonId == seasonId)
.Select(item => new WinnerPublicationSnapshot(
item.CategoryId,
item.Candidate.StreamerIdentityId,
item.Candidate.DisplayName,
item.Candidate.ChannelSlug,
item.Candidate.ClipCompilationUrl))
.ToArrayAsync(cancellationToken);
var categoriesWithResults = resultSnapshots
.Select(item => item.CategoryId)
.Distinct()
.Count();
var missingResults = Math.Max(0, categoryIds.Length - categoriesWithResults);
if (missingResults > 0)
{
issues.Add($"{missingResults} Kategorien haben noch keinen Gewinner.");
}
var openReviewCount = await db.Nominations
.AsNoTracking()
.CountAsync(item => item.SeasonId == seasonId && item.Status == "pending", cancellationToken);
if (openReviewCount > 0)
{
issues.Add($"{openReviewCount} Nominierungs-Review(s) sind noch offen.");
}
var workflowRules = await LoadWorkflowRulesAsync(db, seasonId, cancellationToken);
var winnerRequiresClipRule = WorkflowRuleSettings.Find(workflowRules, WorkflowRuleSettings.WinnerRequiresClip);
if (WorkflowRuleSettings.ShouldBlock(winnerRequiresClipRule))
{
var missingWinnerClipCount = resultSnapshots.Count(item => string.IsNullOrWhiteSpace(item.ClipCompilationUrl));
if (missingWinnerClipCount > 0)
{
issues.Add($"{missingWinnerClipCount} Gewinner haben noch keinen gepflegten Clip-Link.");
}
}
var winnerPlacementsRule = WorkflowRuleSettings.Find(workflowRules, WorkflowRuleSettings.MaxWinnerPlacements);
if (WorkflowRuleSettings.ShouldBlock(winnerPlacementsRule))
{
var winnerOverflow = resultSnapshots
.GroupBy(item => ResolveCandidateIdentityKey(item.StreamerIdentityId, item.DisplayName, item.ChannelSlug))
.Select(group => new
{
Count = group.Count(),
DisplayName = group.Select(item => item.DisplayName).FirstOrDefault(name => !string.IsNullOrWhiteSpace(name)) ?? "Unbekannt",
})
.Where(item => item.Count > winnerPlacementsRule.Limit)
.OrderByDescending(item => item.Count)
.FirstOrDefault();
if (winnerOverflow is not null)
{
issues.Add(
$"Workflow-Regel blockiert: {winnerOverflow.DisplayName} hat bereits {winnerOverflow.Count} Gewinnerplatz(e). Limit: {winnerPlacementsRule.Limit}.");
}
}
return issues.ToArray();
}
private static string ResolveCandidateIdentityKey(int? streamerIdentityId, string displayName, string channelSlug)
{
if (streamerIdentityId.HasValue)