refactor(puzzle): route finish rewards through InventoryService

Replace RewardGrantService + HttpContext.RequestServices viewer load with
IInventoryService tx. Single BeginAsync/GrantAsync/CommitAsync wraps all
mission rewards on the win path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-05-31 16:40:16 -04:00
parent 4ba7d8f6d0
commit a310697830

View File

@@ -1,12 +1,11 @@
using System.Text.Json; using System.Text.Json;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using SVSim.Database; using SVSim.Database;
using SVSim.Database.Enums;
using SVSim.Database.Repositories.Globals; using SVSim.Database.Repositories.Globals;
using SVSim.Database.Repositories.Viewer; using SVSim.Database.Repositories.Viewer;
using SVSim.Database.Services; using SVSim.Database.Services.Inventory;
using SVSim.EmulatedEntrypoint.Models.Dtos.Common.BasicPuzzle; using SVSim.EmulatedEntrypoint.Models.Dtos.Common.BasicPuzzle;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests; using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.BasicPuzzle; using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.BasicPuzzle;
@@ -26,20 +25,20 @@ public class PuzzleController : SVSimController
private readonly IPuzzleCatalogRepository _catalog; private readonly IPuzzleCatalogRepository _catalog;
private readonly IPuzzleClearRepository _clears; private readonly IPuzzleClearRepository _clears;
private readonly PuzzleMissionEvaluator _evaluator; private readonly PuzzleMissionEvaluator _evaluator;
private readonly RewardGrantService _rewards; private readonly IInventoryService _inv;
private readonly ILogger<PuzzleController> _logger; private readonly ILogger<PuzzleController> _logger;
public PuzzleController( public PuzzleController(
IPuzzleCatalogRepository catalog, IPuzzleCatalogRepository catalog,
IPuzzleClearRepository clears, IPuzzleClearRepository clears,
PuzzleMissionEvaluator evaluator, PuzzleMissionEvaluator evaluator,
RewardGrantService rewards, IInventoryService inv,
ILogger<PuzzleController> logger) ILogger<PuzzleController> logger)
{ {
_catalog = catalog; _catalog = catalog;
_clears = clears; _clears = clears;
_evaluator = evaluator; _evaluator = evaluator;
_rewards = rewards; _inv = inv;
_logger = logger; _logger = logger;
} }
@@ -175,28 +174,15 @@ public class PuzzleController : SVSimController
if (fresh.Count > 0) if (fresh.Count > 0)
{ {
// Load viewer with all the collections RewardGrantService might mutate. Split-query await using var tx = await _inv.BeginAsync(viewerId);
// to avoid the cartesian-explode pitfall (CLAUDE.md "EF split query").
var ctx = HttpContext.RequestServices.GetRequiredService<SVSimDbContext>();
var viewer = await ctx.Viewers
.Include(v => v.Cards).ThenInclude(c => c.Card)
.Include(v => v.Sleeves)
.Include(v => v.Emblems)
.Include(v => v.LeaderSkins)
.Include(v => v.Degrees)
.Include(v => v.MyPageBackgrounds)
.Include(v => v.Items).ThenInclude(i => i.Item)
.AsSplitQuery()
.FirstAsync(v => v.Id == viewerId);
foreach (var status in fresh) foreach (var status in fresh)
{ {
IReadOnlyList<GrantedReward> granted; IReadOnlyList<SVSim.Database.Services.GrantedReward> granted;
try try
{ {
granted = await _rewards.ApplyAsync( granted = await tx.GrantAsync(
viewer, (UserGoodsType)status.Mission.RewardType,
(SVSim.Database.Enums.UserGoodsType)status.Mission.RewardType,
status.Mission.RewardDetailId, status.Mission.RewardDetailId,
status.Mission.RewardNumber); status.Mission.RewardNumber);
} }
@@ -229,7 +215,7 @@ public class PuzzleController : SVSimController
} }
} }
await ctx.SaveChangesAsync(); await tx.CommitAsync();
} }
response.WinCount = "1"; response.WinCount = "1";