From 5c6b70327617ec73e40bfef907618c98c8485d7a Mon Sep 17 00:00:00 2001 From: gamer147 Date: Fri, 29 May 2026 14:18:57 -0400 Subject: [PATCH] refactor(itempurchase): route currency spend (not items) through CurrencySpendService Co-Authored-By: Claude Sonnet 4.6 --- .../Controllers/ItemPurchaseController.cs | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/SVSim.EmulatedEntrypoint/Controllers/ItemPurchaseController.cs b/SVSim.EmulatedEntrypoint/Controllers/ItemPurchaseController.cs index 3510c5c..7b1d3b4 100644 --- a/SVSim.EmulatedEntrypoint/Controllers/ItemPurchaseController.cs +++ b/SVSim.EmulatedEntrypoint/Controllers/ItemPurchaseController.cs @@ -23,12 +23,14 @@ public class ItemPurchaseController : SVSimController private readonly SVSimDbContext _db; private readonly RewardGrantService _rewards; private readonly TimeProvider _time; + private readonly ICurrencySpendService _spend; - public ItemPurchaseController(SVSimDbContext db, RewardGrantService rewards, TimeProvider time) + public ItemPurchaseController(SVSimDbContext db, RewardGrantService rewards, TimeProvider time, ICurrencySpendService spend) { _db = db; _rewards = rewards; _time = time; + _spend = spend; } [HttpPost("info")] @@ -117,7 +119,7 @@ public class ItemPurchaseController : SVSimController var rewardList = new List(); // Debit the require side. RewardGrantService is grant-only, so handle this inline. - var debit = TryDebit(viewer, (UserGoodsType)entry.RequireItemType, entry.RequireItemId, entry.RequireItemNum); + var debit = await TryDebit(viewer, (UserGoodsType)entry.RequireItemType, entry.RequireItemId, entry.RequireItemNum); if (debit.Error is not null) return BadRequest(new { error = debit.Error }); if (debit.PostState is not null) rewardList.Add(debit.PostState); @@ -160,29 +162,29 @@ public class ItemPurchaseController : SVSimController /// from the viewer, returning a post-state-aware the client /// uses to refresh its cached count. Returns an error string on insufficient balance. /// - private static (RewardListEntry? PostState, string? Error) TryDebit( + private async Task<(RewardListEntry? PostState, string? Error)> TryDebit( Viewer viewer, UserGoodsType type, long detailId, int num) { switch (type) { case UserGoodsType.RedEther: - if (viewer.Currency.RedEther < (ulong)num) - return (null, "insufficient_red_ether"); - viewer.Currency.RedEther -= (ulong)num; - return (new RewardListEntry { RewardType = 1, RewardId = 0, RewardNum = (int)viewer.Currency.RedEther }, null); - + { + var r = await _spend.TrySpendAsync(viewer, SpendCurrency.RedEther, num); + if (!r.Success) return (null, "insufficient_red_ether"); + return (new RewardListEntry { RewardType = 1, RewardId = 0, RewardNum = (int)r.PostStateTotal }, null); + } case UserGoodsType.Crystal: - if (viewer.Currency.Crystals < (ulong)num) - return (null, "insufficient_crystals"); - viewer.Currency.Crystals -= (ulong)num; - return (new RewardListEntry { RewardType = 2, RewardId = 0, RewardNum = (int)viewer.Currency.Crystals }, null); - + { + var r = await _spend.TrySpendAsync(viewer, SpendCurrency.Crystal, num); + if (!r.Success) return (null, "insufficient_crystals"); + return (new RewardListEntry { RewardType = 2, RewardId = 0, RewardNum = (int)r.PostStateTotal }, null); + } case UserGoodsType.Rupy: - if (viewer.Currency.Rupees < (ulong)num) - return (null, "insufficient_rupees"); - viewer.Currency.Rupees -= (ulong)num; - return (new RewardListEntry { RewardType = 9, RewardId = 0, RewardNum = (int)viewer.Currency.Rupees }, null); - + { + var r = await _spend.TrySpendAsync(viewer, SpendCurrency.Rupee, num); + if (!r.Success) return (null, "insufficient_rupees"); + return (new RewardListEntry { RewardType = 9, RewardId = 0, RewardNum = (int)r.PostStateTotal }, null); + } case UserGoodsType.Item: var owned = viewer.Items.FirstOrDefault(i => i.Item.Id == (int)detailId); if (owned is null || owned.Count < num)