feat(missions): /load/index materializes viewer mission/achievement state
EnsureCurrentAsync now takes viewerId (was Viewer), so it works with LoadController's AsNoTracking-loaded detached viewers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
using SVSim.Database.Models;
|
||||
|
||||
namespace SVSim.EmulatedEntrypoint.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Lazy-initializes viewer mission/achievement state. Idempotent. Called from
|
||||
/// LoadController on every /load/index and as belt-and-braces from /mission/info.
|
||||
/// Takes viewerId (not Viewer) so it works against both tracked and detached viewer loads.
|
||||
/// Caller is responsible for SaveChangesAsync.
|
||||
/// </summary>
|
||||
public interface IViewerMissionStateService
|
||||
{
|
||||
Task EnsureCurrentAsync(Viewer viewer, CancellationToken ct = default);
|
||||
Task EnsureCurrentAsync(long viewerId, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -25,15 +25,13 @@ public sealed class ViewerMissionStateService : IViewerMissionStateService
|
||||
_time = time;
|
||||
}
|
||||
|
||||
public async Task EnsureCurrentAsync(Viewer viewer, CancellationToken ct = default)
|
||||
public async Task EnsureCurrentAsync(long viewerId, CancellationToken ct = default)
|
||||
{
|
||||
// Always read fresh from DB. Navigation properties on the passed viewer may be stale or
|
||||
// double-tracked depending on caller's load path — don't trust them.
|
||||
var existingAchievements = await _viewerRepo.GetAchievementsAsync(viewer.Id, ct);
|
||||
var existingMissions = await _viewerRepo.GetMissionsAsync(viewer.Id, ct);
|
||||
var existingAchievements = await _viewerRepo.GetAchievementsAsync(viewerId, ct);
|
||||
var existingMissions = await _viewerRepo.GetMissionsAsync(viewerId, ct);
|
||||
|
||||
await MaterializeAchievementsAsync(viewer.Id, existingAchievements, ct);
|
||||
await EnsureMissionSlotsAsync(viewer.Id, existingMissions, ct);
|
||||
await MaterializeAchievementsAsync(viewerId, existingAchievements, ct);
|
||||
await EnsureMissionSlotsAsync(viewerId, existingMissions, ct);
|
||||
}
|
||||
|
||||
private async Task MaterializeAchievementsAsync(long viewerId, List<ViewerAchievement> existing, CancellationToken ct)
|
||||
|
||||
Reference in New Issue
Block a user