refactor(achievement): route receive_reward through InventoryService
Replace RewardGrantService with IInventoryService tx. EnsureCurrentAsync still runs before BeginAsync to avoid EF concurrent-context conflicts; tx.Viewer replaces the manually loaded viewer graph. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,8 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.Database.Repositories.Mission;
|
||||
using SVSim.Database.Services;
|
||||
using SVSim.Database.Services.Inventory;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Achievement;
|
||||
using SVSim.EmulatedEntrypoint.Services;
|
||||
|
||||
@@ -22,20 +21,20 @@ public class AchievementController : SVSimController
|
||||
private readonly IMissionCatalogRepository _catalog;
|
||||
private readonly IViewerMissionStateService _state;
|
||||
private readonly IMissionAssembler _assembler;
|
||||
private readonly RewardGrantService _grantService;
|
||||
private readonly IInventoryService _inv;
|
||||
|
||||
public AchievementController(
|
||||
SVSimDbContext db,
|
||||
IMissionCatalogRepository catalog,
|
||||
IViewerMissionStateService state,
|
||||
IMissionAssembler assembler,
|
||||
RewardGrantService grantService)
|
||||
IInventoryService inv)
|
||||
{
|
||||
_db = db;
|
||||
_catalog = catalog;
|
||||
_state = state;
|
||||
_assembler = assembler;
|
||||
_grantService = grantService;
|
||||
_inv = inv;
|
||||
}
|
||||
|
||||
[HttpPost("receive_reward")]
|
||||
@@ -44,21 +43,15 @@ public class AchievementController : SVSimController
|
||||
{
|
||||
if (!TryGetViewerId(out long viewerId)) return Unauthorized();
|
||||
|
||||
// Load viewer with all the collections RewardGrantService may need to mutate.
|
||||
var viewer = await _db.Viewers
|
||||
.Include(v => v.MissionData)
|
||||
.Include(v => v.Currency)
|
||||
.Include(v => v.Cards)
|
||||
.Include(v => v.Items)
|
||||
.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, ct);
|
||||
// EnsureCurrentAsync needs a viewer id — use a lightweight pre-check load then
|
||||
// materialize state before opening the inventory tx.
|
||||
var viewerIdCheck = await _db.Viewers
|
||||
.Where(v => v.Id == viewerId)
|
||||
.Select(v => v.Id)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
if (viewerIdCheck == 0) return Unauthorized();
|
||||
|
||||
await _state.EnsureCurrentAsync(viewer.Id, ct);
|
||||
await _state.EnsureCurrentAsync(viewerId, ct);
|
||||
await _db.SaveChangesAsync(ct);
|
||||
|
||||
// Re-read viewer's achievement for this type after state-service materialization.
|
||||
@@ -75,9 +68,10 @@ public class AchievementController : SVSimController
|
||||
return Ok(new { result_code = FailureResultCode });
|
||||
}
|
||||
|
||||
// Grant via the canonical RewardGrantService primitive.
|
||||
var granted = await _grantService.ApplyAsync(
|
||||
viewer,
|
||||
// Open inventory tx and grant via InventoryService.
|
||||
await using var tx = await _inv.BeginAsync(viewerId, ct);
|
||||
|
||||
var granted = await tx.GrantAsync(
|
||||
(UserGoodsType)catalogRow.RewardType,
|
||||
catalogRow.RewardDetailId,
|
||||
catalogRow.RewardNumber,
|
||||
@@ -99,9 +93,9 @@ public class AchievementController : SVSimController
|
||||
}
|
||||
ach.NowAchievedLevel = request.Level;
|
||||
|
||||
await _db.SaveChangesAsync(ct);
|
||||
await tx.CommitAsync(ct);
|
||||
|
||||
var dto = await _assembler.BuildAsync(viewer, ct);
|
||||
var dto = await _assembler.BuildAsync(tx.Viewer, ct);
|
||||
var resp = new AchievementReceiveRewardResponse
|
||||
{
|
||||
UserMissionList = dto.UserMissionList,
|
||||
|
||||
Reference in New Issue
Block a user