feat(missions): /mission/info, /mission/retire, /mission/change_receive_setting

Three endpoints + 9 integration tests. Captured-data-is-catalog: viewer's
achievement Level starts at MIN(Level) per type from the catalog (not 1),
so the assembler always has a row to render against.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-05-27 10:35:40 -04:00
parent 574e9ca58b
commit b65a437102
7 changed files with 423 additions and 4 deletions

View File

@@ -38,17 +38,19 @@ public sealed class ViewerMissionStateService : IViewerMissionStateService
private async Task MaterializeAchievementsAsync(long viewerId, List<ViewerAchievement> existing, CancellationToken ct)
{
var catalogTypes = await _catalog.GetAllAchievementTypesAsync(ct);
if (catalogTypes.Count == 0) return;
var minLevelByType = await _catalog.GetMinLevelByAchievementTypeAsync(ct);
if (minLevelByType.Count == 0) return;
var existingTypes = existing.Select(a => a.AchievementType).ToHashSet();
foreach (var type in catalogTypes)
foreach (var (type, minLevel) in minLevelByType)
{
if (existingTypes.Contains(type)) continue;
// Start at the lowest captured tier — with captured-data-is-catalog, a "fresh" viewer
// is conceptually at whichever tier we first know about for that type.
_viewerRepo.AddAchievement(new ViewerAchievement
{
ViewerId = viewerId,
AchievementType = type,
Level = 1,
Level = minLevel,
AchievementStatus = 0,
NowAchievedLevel = 0,
ResultAnnounceSawLevel = 0,