refactor(inventory): consolidate IsCurrency, skip num=0 grants in history

- Drop IsWalletCurrency (duplicate of IsCurrency); use IsCurrency in WriteAcquireHistory.
- Add comment on first SaveChangesAsync in CommitAsync explaining the two-phase flush.
- Guard WriteAcquireHistory loop with grant.Num == 0 check so synthetic DebitItem post-state ops do not produce history rows.
- Add InventoryHistoryTests.Commit_writes_no_history_row_for_item_debit to lock in the fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-09 14:37:50 -04:00
parent bea5a1efd4
commit fb1e6829b7
2 changed files with 28 additions and 7 deletions

View File

@@ -276,6 +276,7 @@ internal sealed class InventoryTransaction : IInventoryTransaction
{
ThrowIfCommitted();
// Flush entity mutations first so audit-history rows are staged on top of post-commit state.
await _db.SaveChangesAsync(ct);
WriteAcquireHistory();
@@ -298,10 +299,11 @@ internal sealed class InventoryTransaction : IInventoryTransaction
foreach (var op in _ops)
{
if (op is not GrantOp grant) continue;
if (grant.Num == 0) continue; // skip synthetic post-state grants (e.g. DebitItem)
var rowSource = grant.IsCascade ? GrantSource.CardCosmeticCascade : _source;
var rowMessage = grant.IsCascade ? cascadeMessage : primaryMessage;
var detailId = IsWalletCurrency(grant.Type) ? 0L : grant.DetailId;
var detailId = IsCurrency(grant.Type) ? 0L : grant.DetailId;
_db.ViewerAcquireHistory.Add(new ViewerAcquireHistoryEntry
{
@@ -316,12 +318,6 @@ internal sealed class InventoryTransaction : IInventoryTransaction
}
}
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