Skip to content

Commit

Permalink
Merge pull request #134 from planetarium/release/16
Browse files Browse the repository at this point in the history
Release/16
  • Loading branch information
ipdae authored Nov 28, 2024
2 parents eae69fe + 6d0ca85 commit 6013d52
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 51 deletions.
6 changes: 3 additions & 3 deletions MarketService.Tests/MarketControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public async void GetItemProducts()
SizeLimit = null,
});
var controller = new MarketController(_logger, _context, cache);
var response = await controller.GetItemProducts((int) ItemSubType.Armor, null, null, null, null, Array.Empty<int>(), false);
var response = await controller.GetItemProducts((int) ItemSubType.Armor, null, null, null, null, Array.Empty<int>(), Array.Empty<int>(), false);
var result = Assert.Single(response.ItemProducts);
Assert.IsType<ItemProductResponseModel>(result);
Assert.Equal(product.ProductId, result.ProductId);
Expand Down Expand Up @@ -227,14 +227,14 @@ public async void GetItemProductsByStat()
var controller = new MarketController(_logger, _context, cache);
foreach (var stat in new [] {"atk", "ATK", "HP", "hp", "Hp", "Atk", null })
{
var response = await controller.GetItemProducts((int) ItemSubType.Armor, null, null, null, stat, Array.Empty<int>(), false);
var response = await controller.GetItemProducts((int) ItemSubType.Armor, null, null, null, stat, Array.Empty<int>(), Array.Empty<int>(), false);
var result = Assert.Single(response.ItemProducts);
Assert.IsType<ItemProductResponseModel>(result);
Assert.Equal(product.ProductId, result.ProductId);
Assert.Equal(2, result.Quantity);
Assert.Equal(3, result.Price);
}
var response2 = await controller.GetItemProducts((int) ItemSubType.Armor, null, null, null, "DEF", Array.Empty<int>(), false);
var response2 = await controller.GetItemProducts((int) ItemSubType.Armor, null, null, null, "DEF", Array.Empty<int>(), Array.Empty<int>(), false);
Assert.Empty(response2.ItemProducts);
await _context.Database.EnsureDeletedAsync();
}
Expand Down
118 changes: 70 additions & 48 deletions MarketService/Controllers/MarketController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ public MarketController(ILogger<MarketController> logger, MarketContext marketCo
}

[HttpGet("products/items/{type}")]
public async Task<MarketProductResponse> GetItemProducts(int type, int? limit, int? offset, string? order, string? stat, [FromQuery] int[] iconIds, bool isCustom)
public async Task<MarketProductResponse> GetItemProducts(int type, int? limit, int? offset, string? order,
string? stat, [FromQuery] int[] itemIds, [FromQuery] int[] iconIds, bool isCustom)
{
var itemSubType = (ItemSubType) type;
var queryOffset = offset ?? 0;
var queryLimit = limit ?? 100;
var sort = string.IsNullOrEmpty(order) ? "cp_desc" : order;
var statType = string.IsNullOrEmpty(stat) ? StatType.NONE : Enum.Parse<StatType>(stat, true);
var queryResult = await Get(itemSubType, queryLimit, queryOffset, sort, statType, iconIds, isCustom);
var queryResult = await Get(itemSubType, queryLimit, queryOffset, sort, statType, itemIds, iconIds, isCustom);
var totalCount = queryResult.Count;
return new MarketProductResponse(
totalCount,
Expand All @@ -46,59 +47,80 @@ public async Task<MarketProductResponse> GetItemProducts(int type, int? limit, i
.Take(queryLimit));
}

/// <summary>
/// Query and get filtered item list from market service.
/// </summary>
/// <param name="itemSubType">Item subtype to query.</param>
/// <param name="queryLimit">Result count limit.</param>
/// <param name="queryOffset">Offset to find from.</param>
/// <param name="sort">Type of sorting result.</param>
/// <param name="statType">Target stat type to query.</param>
/// <param name="itemIds">Item IDs to filter. This will be ignored when `iconIds` comes in.</param>
/// <param name="iconIds">Icon IDs to filter. This only works for equipment. This will be solely used when both IconIds and ItemIds are incoming.</param>
/// <param name="isCustom">Flag to find from custom craft.</param>
/// <returns></returns>
private async Task<List<ItemProductModel>> Get(ItemSubType itemSubType, int queryLimit, int queryOffset,
string sort, StatType statType, int[] iconIds, bool isCustom)
string sort, StatType statType, int[] itemIds, int[] iconIds, bool isCustom)
{
var ids = string.Join("_", iconIds.OrderBy(i => i));
var cacheKey = $"{itemSubType}_{queryLimit}_{queryOffset}_{sort}_{statType}_{ids}_{isCustom}";
if (!_memoryCache.TryGetValue(cacheKey, out List<ItemProductModel>? queryResult))
{
var query = _dbContext.ItemProducts
.AsNoTracking()
.Include(p => p.Skills)
.Include(p => p.Stats)
.Where(p => p.ItemSubType == itemSubType && p.Exist);
if (statType != StatType.NONE)
{
query = query.Where(p => p.Stats.Any(s => s.Type == statType));
}
var iconIdKey = string.Join("_", iconIds.OrderBy(i => i));
var iconCacheKey = $"{itemSubType}_{queryLimit}_{queryOffset}_{sort}_{statType}_{iconIdKey}_{isCustom}";
var itemIdKey = string.Join("_", itemIds.OrderBy(i => i));
var itemCacheKey = $"{itemSubType}_{queryLimit}_{queryOffset}_{sort}_{statType}_{itemIdKey}";

if (iconIds.Any())
{
query = query.Where(p => iconIds.Contains(p.IconId));
}
if (_memoryCache.TryGetValue(iconIds.Any() ? iconCacheKey : itemCacheKey,
out List<ItemProductModel>? queryResult))
return queryResult!;

if (isCustom)
{
query = query.Where(p => p.ByCustomCraft);
}
var query = _dbContext.ItemProducts
.AsNoTracking()
.Include(p => p.Skills)
.Include(p => p.Stats)
.Where(p => p.ItemSubType == itemSubType && p.Exist);
if (statType != StatType.NONE)
{
query = query.Where(p => p.Stats.Any(s => s.Type == statType));
}

query = query.AsSingleQuery();
query = sort switch
{
"cp_desc" => query.OrderByDescending(p => p.CombatPoint),
"cp" => query.OrderBy(p => p.CombatPoint),
"price_desc" => query.OrderByDescending(p => p.Price),
"price" => query.OrderBy(p => p.Price),
"grade_desc" => query.OrderByDescending(p => p.Grade),
"grade" => query.OrderBy(p => p.Grade),
"crystal_desc" => query.OrderByDescending(p => p.Crystal),
"crystal" => query.OrderBy(p => p.Crystal),
"crystal_per_price_desc" => query.OrderByDescending(p => p.CrystalPerPrice).ThenByDescending(p => p.Crystal),
"crystal_per_price" => query.OrderBy(p => p.CrystalPerPrice).ThenBy(p => p.Crystal),
"level_desc" => query.OrderByDescending(p => p.Level),
"level" => query.OrderBy(p => p.Level),
"opt_count_desc" => query.OrderByDescending(p => p.OptionCountFromCombination),
"opt_count" => query.OrderBy(p => p.OptionCountFromCombination),
"unit_price_desc" => query.OrderByDescending(p => p.UnitPrice).ThenByDescending(p => p.Price),
"unit_price" => query.OrderBy(p => p.UnitPrice).ThenBy(p => p.Price),
_ => query
};
var result = await query.ToListAsync();
_memoryCache.Set(cacheKey, result, _cacheTime);
queryResult = result;
// Use iconIds when present. Ignore itemIds in this case.
if (iconIds.Any())
{
query = query.Where(p => iconIds.Contains(p.IconId));
}
else if (itemIds.Any())
{
query = query.Where(p => itemIds.Contains(p.ItemId));
}

if (isCustom)
{
query = query.Where(p => p.ByCustomCraft);
}

query = query.AsSingleQuery();
query = sort switch
{
"cp_desc" => query.OrderByDescending(p => p.CombatPoint),
"cp" => query.OrderBy(p => p.CombatPoint),
"price_desc" => query.OrderByDescending(p => p.Price),
"price" => query.OrderBy(p => p.Price),
"grade_desc" => query.OrderByDescending(p => p.Grade),
"grade" => query.OrderBy(p => p.Grade),
"crystal_desc" => query.OrderByDescending(p => p.Crystal),
"crystal" => query.OrderBy(p => p.Crystal),
"crystal_per_price_desc" => query.OrderByDescending(p => p.CrystalPerPrice)
.ThenByDescending(p => p.Crystal),
"crystal_per_price" => query.OrderBy(p => p.CrystalPerPrice).ThenBy(p => p.Crystal),
"level_desc" => query.OrderByDescending(p => p.Level),
"level" => query.OrderBy(p => p.Level),
"opt_count_desc" => query.OrderByDescending(p => p.OptionCountFromCombination),
"opt_count" => query.OrderBy(p => p.OptionCountFromCombination),
"unit_price_desc" => query.OrderByDescending(p => p.UnitPrice).ThenByDescending(p => p.Price),
"unit_price" => query.OrderBy(p => p.UnitPrice).ThenBy(p => p.Price),
_ => query
};
var result = await query.ToListAsync();
_memoryCache.Set(iconCacheKey, result, _cacheTime);
queryResult = result;
return queryResult!;
}

Expand Down

0 comments on commit 6013d52

Please sign in to comment.