feat(inventory): write acquire history rows on commit

CommitAsync now calls WriteAcquireHistory() between the two SaveChanges
calls: iterates _ops, skips SpendOps, writes one ViewerAcquireHistoryEntry
per GrantOp. Cascade rows get GrantSource.CardCosmeticCascade; wallet
currencies zero RewardDetailId; all rows in a single commit share one
DateTime.UtcNow timestamp. Closes _source plumbing from Task 5.

5 new tests added (46 total inventory, 0 failures).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-09 14:32:51 -04:00
parent 015c7ce259
commit bea5a1efd4
2 changed files with 187 additions and 0 deletions

View File

@@ -277,6 +277,10 @@ internal sealed class InventoryTransaction : IInventoryTransaction
ThrowIfCommitted();
await _db.SaveChangesAsync(ct);
WriteAcquireHistory();
await _db.SaveChangesAsync(ct);
await _dbTx.CommitAsync(ct);
_committed = true;
@@ -285,6 +289,39 @@ internal sealed class InventoryTransaction : IInventoryTransaction
return new InventoryCommitResult(rewardList, deltas);
}
private void WriteAcquireHistory()
{
var now = DateTime.UtcNow;
var primaryMessage = GrantSourceMessages.For(_source);
var cascadeMessage = GrantSourceMessages.For(GrantSource.CardCosmeticCascade);
foreach (var op in _ops)
{
if (op is not GrantOp grant) continue;
var rowSource = grant.IsCascade ? GrantSource.CardCosmeticCascade : _source;
var rowMessage = grant.IsCascade ? cascadeMessage : primaryMessage;
var detailId = IsWalletCurrency(grant.Type) ? 0L : grant.DetailId;
_db.ViewerAcquireHistory.Add(new ViewerAcquireHistoryEntry
{
ViewerId = Viewer.Id,
RewardType = (int)grant.Type,
RewardDetailId = detailId,
RewardCount = grant.Num,
AcquireType = (int)rowSource,
Message = rowMessage,
AcquireTime = now,
});
}
}
private static bool IsWalletCurrency(UserGoodsType type) => type
is UserGoodsType.Crystal
or UserGoodsType.Rupy
or UserGoodsType.RedEther
or UserGoodsType.SpotCardPoint;
private IReadOnlyList<GrantedReward> BuildRewardList()
{
// Pass 1 — for each currency type, find the last op (spend OR grant) that touched it