refactor(battlepass): route premium-buy crystal spend through CurrencySpendService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-05-29 14:23:50 -04:00
parent ee407befb5
commit d68a85bbc5

View File

@@ -23,19 +23,22 @@ public sealed class BattlePassService : IBattlePassService
private readonly TimeProvider _time; private readonly TimeProvider _time;
private readonly SVSimDbContext _db; private readonly SVSimDbContext _db;
private readonly RewardGrantService _rewards; private readonly RewardGrantService _rewards;
private readonly ICurrencySpendService _spend;
public BattlePassService( public BattlePassService(
IBattlePassRepository bp, IBattlePassRepository bp,
IViewerBattlePassRepository viewerBp, IViewerBattlePassRepository viewerBp,
TimeProvider time, TimeProvider time,
SVSimDbContext db, SVSimDbContext db,
RewardGrantService rewards) RewardGrantService rewards,
ICurrencySpendService spend)
{ {
_bp = bp; _bp = bp;
_viewerBp = viewerBp; _viewerBp = viewerBp;
_time = time; _time = time;
_db = db; _db = db;
_rewards = rewards; _rewards = rewards;
_spend = spend;
} }
public async Task<IReadOnlyDictionary<string, BattlePassLevel>?> GetLevelCurveAsync(CancellationToken ct) public async Task<IReadOnlyDictionary<string, BattlePassLevel>?> GetLevelCurveAsync(CancellationToken ct)
@@ -166,13 +169,13 @@ public sealed class BattlePassService : IBattlePassService
if (progress.IsPremium) if (progress.IsPremium)
return new BattlePassBuyOutcome(23, Array.Empty<GrantedReward>(), Array.Empty<GrantedReward>()); return new BattlePassBuyOutcome(23, Array.Empty<GrantedReward>(), Array.Empty<GrantedReward>());
if (viewer.Currency.Crystals < (ulong)season.PriceCrystal) var spendResult = await _spend.TrySpendAsync(viewer, SpendCurrency.Crystal, season.PriceCrystal, ct);
if (!spendResult.Success)
return new BattlePassBuyOutcome(22, Array.Empty<GrantedReward>(), Array.Empty<GrantedReward>()); return new BattlePassBuyOutcome(22, Array.Empty<GrantedReward>(), Array.Empty<GrantedReward>());
// BeginTransactionAsync is a no-op on the SQLite in-memory test DB but is safe to call. // BeginTransactionAsync is a no-op on the SQLite in-memory test DB but is safe to call.
await using var tx = await _db.Database.BeginTransactionAsync(ct); await using var tx = await _db.Database.BeginTransactionAsync(ct);
viewer.Currency.Crystals -= (ulong)season.PriceCrystal;
progress.IsPremium = true; progress.IsPremium = true;
// Retroactive grants: every premium reward at level <= current_level not already claimed. // Retroactive grants: every premium reward at level <= current_level not already claimed.
@@ -206,7 +209,7 @@ public sealed class BattlePassService : IBattlePassService
// append the post-deduction total so the client gets the correct final balance. // append the post-deduction total so the client gets the correct final balance.
postState.RemoveAll(r => r.RewardType == (int)UserGoodsType.Crystal); postState.RemoveAll(r => r.RewardType == (int)UserGoodsType.Crystal);
postState.Add(new GrantedReward( postState.Add(new GrantedReward(
(int)UserGoodsType.Crystal, 0, (int)viewer.Currency.Crystals)); (int)UserGoodsType.Crystal, 0, (int)spendResult.PostStateTotal));
return new BattlePassBuyOutcome(1, achieved, postState); return new BattlePassBuyOutcome(1, achieved, postState);
} }