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 Microsoft.EntityFrameworkCore;
|
||||||
using SVSim.Database;
|
using SVSim.Database;
|
||||||
using SVSim.Database.Enums;
|
using SVSim.Database.Enums;
|
||||||
using SVSim.Database.Models;
|
|
||||||
using SVSim.Database.Repositories.Mission;
|
using SVSim.Database.Repositories.Mission;
|
||||||
using SVSim.Database.Services;
|
using SVSim.Database.Services.Inventory;
|
||||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Achievement;
|
using SVSim.EmulatedEntrypoint.Models.Dtos.Achievement;
|
||||||
using SVSim.EmulatedEntrypoint.Services;
|
using SVSim.EmulatedEntrypoint.Services;
|
||||||
|
|
||||||
@@ -22,20 +21,20 @@ public class AchievementController : SVSimController
|
|||||||
private readonly IMissionCatalogRepository _catalog;
|
private readonly IMissionCatalogRepository _catalog;
|
||||||
private readonly IViewerMissionStateService _state;
|
private readonly IViewerMissionStateService _state;
|
||||||
private readonly IMissionAssembler _assembler;
|
private readonly IMissionAssembler _assembler;
|
||||||
private readonly RewardGrantService _grantService;
|
private readonly IInventoryService _inv;
|
||||||
|
|
||||||
public AchievementController(
|
public AchievementController(
|
||||||
SVSimDbContext db,
|
SVSimDbContext db,
|
||||||
IMissionCatalogRepository catalog,
|
IMissionCatalogRepository catalog,
|
||||||
IViewerMissionStateService state,
|
IViewerMissionStateService state,
|
||||||
IMissionAssembler assembler,
|
IMissionAssembler assembler,
|
||||||
RewardGrantService grantService)
|
IInventoryService inv)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_catalog = catalog;
|
_catalog = catalog;
|
||||||
_state = state;
|
_state = state;
|
||||||
_assembler = assembler;
|
_assembler = assembler;
|
||||||
_grantService = grantService;
|
_inv = inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("receive_reward")]
|
[HttpPost("receive_reward")]
|
||||||
@@ -44,21 +43,15 @@ public class AchievementController : SVSimController
|
|||||||
{
|
{
|
||||||
if (!TryGetViewerId(out long viewerId)) return Unauthorized();
|
if (!TryGetViewerId(out long viewerId)) return Unauthorized();
|
||||||
|
|
||||||
// Load viewer with all the collections RewardGrantService may need to mutate.
|
// EnsureCurrentAsync needs a viewer id — use a lightweight pre-check load then
|
||||||
var viewer = await _db.Viewers
|
// materialize state before opening the inventory tx.
|
||||||
.Include(v => v.MissionData)
|
var viewerIdCheck = await _db.Viewers
|
||||||
.Include(v => v.Currency)
|
.Where(v => v.Id == viewerId)
|
||||||
.Include(v => v.Cards)
|
.Select(v => v.Id)
|
||||||
.Include(v => v.Items)
|
.FirstOrDefaultAsync(ct);
|
||||||
.Include(v => v.Sleeves)
|
if (viewerIdCheck == 0) return Unauthorized();
|
||||||
.Include(v => v.Emblems)
|
|
||||||
.Include(v => v.Degrees)
|
|
||||||
.Include(v => v.LeaderSkins)
|
|
||||||
.Include(v => v.MyPageBackgrounds)
|
|
||||||
.AsSplitQuery()
|
|
||||||
.FirstAsync(v => v.Id == viewerId, ct);
|
|
||||||
|
|
||||||
await _state.EnsureCurrentAsync(viewer.Id, ct);
|
await _state.EnsureCurrentAsync(viewerId, ct);
|
||||||
await _db.SaveChangesAsync(ct);
|
await _db.SaveChangesAsync(ct);
|
||||||
|
|
||||||
// Re-read viewer's achievement for this type after state-service materialization.
|
// 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 });
|
return Ok(new { result_code = FailureResultCode });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grant via the canonical RewardGrantService primitive.
|
// Open inventory tx and grant via InventoryService.
|
||||||
var granted = await _grantService.ApplyAsync(
|
await using var tx = await _inv.BeginAsync(viewerId, ct);
|
||||||
viewer,
|
|
||||||
|
var granted = await tx.GrantAsync(
|
||||||
(UserGoodsType)catalogRow.RewardType,
|
(UserGoodsType)catalogRow.RewardType,
|
||||||
catalogRow.RewardDetailId,
|
catalogRow.RewardDetailId,
|
||||||
catalogRow.RewardNumber,
|
catalogRow.RewardNumber,
|
||||||
@@ -99,9 +93,9 @@ public class AchievementController : SVSimController
|
|||||||
}
|
}
|
||||||
ach.NowAchievedLevel = request.Level;
|
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
|
var resp = new AchievementReceiveRewardResponse
|
||||||
{
|
{
|
||||||
UserMissionList = dto.UserMissionList,
|
UserMissionList = dto.UserMissionList,
|
||||||
|
|||||||
Reference in New Issue
Block a user