refactor(gacha-point): route TryExchangeAsync through IInventoryTransaction

Change signature from (Viewer, packId, cardId) to (IInventoryTransaction, packId, cardId).
Drop RewardGrantService from GachaPointService ctor. PackController.ExchangeGachaPoint opens
tx with GachaPointBalances/Received extra includes, passes tx, commits on success.
Update GachaPointServiceTests to use inv.BeginAsync + tx pattern.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-05-31 16:55:08 -04:00
parent b6bf9b7495
commit c37c04c1b7
4 changed files with 60 additions and 60 deletions

View File

@@ -202,26 +202,18 @@ public class PackController : SVSimController
{
if (!TryGetViewerId(out long viewerId)) return Unauthorized();
// Load the viewer with the collections the service mutates (balances, received marker,
// cards, cosmetics). AsSplitQuery per project_ef_split_query memory.
var viewer = await _db.Viewers
.Include(v => v.GachaPointBalances)
.Include(v => v.GachaPointReceived)
.Include(v => v.Cards)
.Include(v => v.Sleeves)
.Include(v => v.Emblems)
.Include(v => v.Degrees)
.Include(v => v.LeaderSkins)
.Include(v => v.MyPageBackgrounds)
.AsSplitQuery()
.FirstAsync(v => v.Id == viewerId);
// Open inventory tx with extra includes for GachaPointBalances + GachaPointReceived
// (needed by TryExchangeAsync to validate balance and already-received guard).
await using var tx = await _inv.BeginAsync(viewerId, configure: cfg => cfg
.WithInclude(v => v.GachaPointBalances)
.WithInclude(v => v.GachaPointReceived));
// Use odds_gacha_id (the seasonal pack id) — that's where the balance / received marker
// live. Mirrors the GetGachaPointRewards fix.
var outcome = await _gachaPoint.TryExchangeAsync(viewer, request.OddsGachaId, request.CardId);
var outcome = await _gachaPoint.TryExchangeAsync(tx, request.OddsGachaId, request.CardId);
if (!outcome.Success) return BadRequest(new { error = outcome.Error });
await _db.SaveChangesAsync();
await tx.CommitAsync();
return new ExchangeGachaPointResponse
{