[Feature] Antag purchase list - #1184
Conversation
WalkthroughДобавлена история покупок антагонистов. Система сохраняет стоимость покупок, обрабатывает возвраты и добавляет данные в итоговый текст целей. Клиент отображает товары, иконки и tooltip. Интеграционный тест проверяет серверный и клиентский сценарии. ChangesИстория покупок и контракты
Estimated code review effort: 4 (Complex) | ~45 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Content.Client/_White/AntagPurchaseHistory/AntagPurchaseTag.cs`:
- Around line 142-151: Stabilize the currency order in the tooltip by ordering
the dictionary entries by pair.Key.Id before the Select in the AntagPurchaseTag
price formatting expression. Preserve the existing currency lookup and localized
display behavior after sorting.
In `@Content.Server/_White/AntagPurchaseHistory/AntagPurchaseHistorySystem.cs`:
- Around line 94-137: Добавьте сериализованный OriginalCost в ключ группировки
groupedPurchases рядом с ListingId и FinalCost, чтобы покупки с разной исходной
ценой попадали в разные группы. В цикле формирования markup используйте значение
OriginalCost из ключа группы вместо purchase.OriginalCost из group.First(),
сохранив остальные атрибуты без изменений.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3be8fd0e-ddff-42dd-8a22-0fdc900f4a24
📒 Files selected for processing (10)
Content.Client/_White/AntagPurchaseHistory/AntagPurchaseTag.csContent.IntegrationTests/Tests/_White/AntagPurchaseHistoryTests.csContent.Server/Objectives/ObjectivesSystem.csContent.Server/Store/Systems/StoreSystem.Ui.csContent.Server/_White/AntagPurchaseHistory/AntagPurchaseHistorySystem.csContent.Shared/Store/StoreBuyFinishedEvent.csContent.Shared/_White/AntagPurchaseHistory/AntagPurchaseHistoryComponent.csContent.Shared/_White/AntagPurchaseHistory/AntagPurchaseMarkup.csResources/Locale/en-US/_white/objectives/antag-purchase-history.ftlResources/Locale/ru-RU/_white/objectives/antag-purchase-history.ftl
| return string.Join(", ", cost.Select(pair => | ||
| { | ||
| if (!_prototypes.TryIndex(pair.Key, out CurrencyPrototype? currency)) | ||
| return $"{pair.Value} {pair.Key.Id}"; | ||
|
|
||
| return Loc.GetString( | ||
| "store-ui-price-display", | ||
| ("amount", pair.Value), | ||
| ("currency", Loc.GetString(currency.DisplayName, ("amount", pair.Value)))); | ||
| })); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Стабилизируйте порядок валют в tooltip.
Серверный GetPriceString в Content.Server/_White/AntagPurchaseHistory/AntagPurchaseHistorySystem.cs сортирует валюты по Id. Клиентский код перечисляет Dictionary без сортировки. Поэтому порядок цен в tooltip может не совпасть с итоговым текстом раунда для одной покупки с несколькими валютами.
Добавьте OrderBy(pair => pair.Key.Id) перед Select.
Предлагаемое исправление
- return string.Join(", ", cost.Select(pair =>
+ return string.Join(", ", cost
+ .OrderBy(pair => pair.Key.Id)
+ .Select(pair =>
{
if (!_prototypes.TryIndex(pair.Key, out CurrencyPrototype? currency))
return $"{pair.Value} {pair.Key.Id}";
return Loc.GetString(
"store-ui-price-display",
("amount", pair.Value),
("currency", Loc.GetString(currency.DisplayName, ("amount", pair.Value))));
- }));
+ }));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return string.Join(", ", cost.Select(pair => | |
| { | |
| if (!_prototypes.TryIndex(pair.Key, out CurrencyPrototype? currency)) | |
| return $"{pair.Value} {pair.Key.Id}"; | |
| return Loc.GetString( | |
| "store-ui-price-display", | |
| ("amount", pair.Value), | |
| ("currency", Loc.GetString(currency.DisplayName, ("amount", pair.Value)))); | |
| })); | |
| return string.Join(", ", cost | |
| .OrderBy(pair => pair.Key.Id) | |
| .Select(pair => | |
| { | |
| if (!_prototypes.TryIndex(pair.Key, out CurrencyPrototype? currency)) | |
| return $"{pair.Value} {pair.Key.Id}"; | |
| return Loc.GetString( | |
| "store-ui-price-display", | |
| ("amount", pair.Value), | |
| ("currency", Loc.GetString(currency.DisplayName, ("amount", pair.Value)))); | |
| })); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Client/_White/AntagPurchaseHistory/AntagPurchaseTag.cs` around lines
142 - 151, Stabilize the currency order in the tooltip by ordering the
dictionary entries by pair.Key.Id before the Select in the AntagPurchaseTag
price formatting expression. Preserve the existing currency lookup and localized
display behavior after sorting.
| var groupedPurchases = purchases | ||
| .GroupBy(purchase => ( | ||
| purchase.ListingId, | ||
| FinalCost: AntagPurchaseMarkup.SerializeCost(purchase.FinalCost))) | ||
| .ToList(); | ||
|
|
||
| var totalCost = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(); | ||
| foreach (var purchase in purchases) | ||
| { | ||
| foreach (var (currency, amount) in purchase.FinalCost) | ||
| totalCost[currency] = totalCost.GetValueOrDefault(currency) + amount; | ||
| } | ||
|
|
||
| var message = new FormattedMessage(); | ||
| message.AddText(Loc.GetString( | ||
| "antag-purchase-history-used", | ||
| ("amounts", GetPriceString(totalCost)))); | ||
| message.AddText(" "); | ||
|
|
||
| for (var i = 0; i < groupedPurchases.Count; i++) | ||
| { | ||
| var group = groupedPurchases[i]; | ||
| var purchase = group.First(); | ||
|
|
||
| if (i > 0) | ||
| message.AddText(", "); | ||
|
|
||
| // The opening bracket must be escaped because this FormattedMessage is converted back to markup | ||
| // before it is parsed by the client. | ||
| message.AddText(FormattedMessage.EscapeText("[")); | ||
| if (group.Count() > 1) | ||
| message.AddText($"{group.Count()}x "); | ||
|
|
||
| var attributes = new Dictionary<string, MarkupParameter> | ||
| { | ||
| [AntagPurchaseMarkup.FinalCostAttribute] = new(group.Key.FinalCost), | ||
| [AntagPurchaseMarkup.OriginalCostAttribute] = new( | ||
| AntagPurchaseMarkup.SerializeCost(purchase.OriginalCost)), | ||
| }; | ||
| message.PushTag( | ||
| new MarkupNode( | ||
| AntagPurchaseMarkup.TagName, | ||
| new MarkupParameter(purchase.ListingId.Id), | ||
| attributes), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Добавьте OriginalCost в ключ группировки.
Группа содержит записи с одинаковыми ListingId и FinalCost. Строки 130-131 передают OriginalCost только из group.First().
Если исходная цена или скидка изменилась между покупками, tooltip покажет исходную цену первой записи для всей группы. Это даёт неверную информацию о скидке. Сериализуйте OriginalCost в ключе группы и используйте это значение в атрибуте markup.
Предлагаемое исправление
var groupedPurchases = purchases
.GroupBy(purchase => (
purchase.ListingId,
- FinalCost: AntagPurchaseMarkup.SerializeCost(purchase.FinalCost)))
+ FinalCost: AntagPurchaseMarkup.SerializeCost(purchase.FinalCost),
+ OriginalCost: AntagPurchaseMarkup.SerializeCost(purchase.OriginalCost)))
.ToList();
@@
[AntagPurchaseMarkup.FinalCostAttribute] = new(group.Key.FinalCost),
- [AntagPurchaseMarkup.OriginalCostAttribute] = new(
- AntagPurchaseMarkup.SerializeCost(purchase.OriginalCost)),
+ [AntagPurchaseMarkup.OriginalCostAttribute] = new(group.Key.OriginalCost),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var groupedPurchases = purchases | |
| .GroupBy(purchase => ( | |
| purchase.ListingId, | |
| FinalCost: AntagPurchaseMarkup.SerializeCost(purchase.FinalCost))) | |
| .ToList(); | |
| var totalCost = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(); | |
| foreach (var purchase in purchases) | |
| { | |
| foreach (var (currency, amount) in purchase.FinalCost) | |
| totalCost[currency] = totalCost.GetValueOrDefault(currency) + amount; | |
| } | |
| var message = new FormattedMessage(); | |
| message.AddText(Loc.GetString( | |
| "antag-purchase-history-used", | |
| ("amounts", GetPriceString(totalCost)))); | |
| message.AddText(" "); | |
| for (var i = 0; i < groupedPurchases.Count; i++) | |
| { | |
| var group = groupedPurchases[i]; | |
| var purchase = group.First(); | |
| if (i > 0) | |
| message.AddText(", "); | |
| // The opening bracket must be escaped because this FormattedMessage is converted back to markup | |
| // before it is parsed by the client. | |
| message.AddText(FormattedMessage.EscapeText("[")); | |
| if (group.Count() > 1) | |
| message.AddText($"{group.Count()}x "); | |
| var attributes = new Dictionary<string, MarkupParameter> | |
| { | |
| [AntagPurchaseMarkup.FinalCostAttribute] = new(group.Key.FinalCost), | |
| [AntagPurchaseMarkup.OriginalCostAttribute] = new( | |
| AntagPurchaseMarkup.SerializeCost(purchase.OriginalCost)), | |
| }; | |
| message.PushTag( | |
| new MarkupNode( | |
| AntagPurchaseMarkup.TagName, | |
| new MarkupParameter(purchase.ListingId.Id), | |
| attributes), | |
| var groupedPurchases = purchases | |
| .GroupBy(purchase => ( | |
| purchase.ListingId, | |
| FinalCost: AntagPurchaseMarkup.SerializeCost(purchase.FinalCost), | |
| OriginalCost: AntagPurchaseMarkup.SerializeCost(purchase.OriginalCost))) | |
| .ToList(); | |
| var totalCost = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(); | |
| foreach (var purchase in purchases) | |
| { | |
| foreach (var (currency, amount) in purchase.FinalCost) | |
| totalCost[currency] = totalCost.GetValueOrDefault(currency) + amount; | |
| } | |
| var message = new FormattedMessage(); | |
| message.AddText(Loc.GetString( | |
| "antag-purchase-history-used", | |
| ("amounts", GetPriceString(totalCost)))); | |
| message.AddText(" "); | |
| for (var i = 0; i < groupedPurchases.Count; i++) | |
| { | |
| var group = groupedPurchases[i]; | |
| var purchase = group.First(); | |
| if (i > 0) | |
| message.AddText(", "); | |
| // The opening bracket must be escaped because this FormattedMessage is converted back to markup | |
| // before it is parsed by the client. | |
| message.AddText(FormattedMessage.EscapeText("[")); | |
| if (group.Count() > 1) | |
| message.AddText($"{group.Count()}x "); | |
| var attributes = new Dictionary<string, MarkupParameter> | |
| { | |
| [AntagPurchaseMarkup.FinalCostAttribute] = new(group.Key.FinalCost), | |
| [AntagPurchaseMarkup.OriginalCostAttribute] = new(group.Key.OriginalCost), | |
| }; | |
| message.PushTag( | |
| new MarkupNode( | |
| AntagPurchaseMarkup.TagName, | |
| new MarkupParameter(purchase.ListingId.Id), | |
| attributes), |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/_White/AntagPurchaseHistory/AntagPurchaseHistorySystem.cs`
around lines 94 - 137, Добавьте сериализованный OriginalCost в ключ группировки
groupedPurchases рядом с ListingId и FinalCost, чтобы покупки с разной исходной
ценой попадали в разные группы. В цикле формирования markup используйте значение
OriginalCost из ключа группы вместо purchase.OriginalCost из group.First(),
сохранив остальные атрибуты без изменений.
Описание PR
В конце раунда в манифесте у антагонистов отображаются покупки и общая потраченная сумма валюты. Поддерживает скидки и возвраты. Группирует покупки по листингу и уплаченной цене.
Медиа
Список
Изменения
🆑 DEADISKO