feat(pack): wire real gacha-point balance into /pack/info (skip ticket-only packs)
This commit is contained in:
@@ -80,18 +80,48 @@ public class PackController : SVSimController
|
|||||||
.Select(i => new { ItemId = (long)EF.Property<int>(i, "ItemId"), i.Count })
|
.Select(i => new { ItemId = (long)EF.Property<int>(i, "ItemId"), i.Count })
|
||||||
.ToDictionaryAsync(x => x.ItemId, x => x.Count);
|
.ToDictionaryAsync(x => x.ItemId, x => x.Count);
|
||||||
|
|
||||||
|
var gachaPointBalancesByPackId = await _db.Viewers
|
||||||
|
.Where(v => v.Id == viewerId)
|
||||||
|
.SelectMany(v => v.GachaPointBalances)
|
||||||
|
.Select(b => new { b.PackId, b.Points })
|
||||||
|
.ToDictionaryAsync(x => x.PackId, x => x.Points);
|
||||||
|
|
||||||
return new PackInfoResponse
|
return new PackInfoResponse
|
||||||
{
|
{
|
||||||
PackConfigList = packs.Select(p => ToDto(p, openCounts, ownedItemsByItemId)).ToList(),
|
PackConfigList = packs
|
||||||
|
.Select(p => ToDto(p, openCounts, ownedItemsByItemId, gachaPointBalancesByPackId))
|
||||||
|
.ToList(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PackConfigDto ToDto(
|
private static PackConfigDto ToDto(
|
||||||
PackConfigEntry p,
|
PackConfigEntry p,
|
||||||
IReadOnlyDictionary<int, ViewerPackOpenCount> openCounts,
|
IReadOnlyDictionary<int, ViewerPackOpenCount> openCounts,
|
||||||
IReadOnlyDictionary<long, int> ownedItemsByItemId)
|
IReadOnlyDictionary<long, int> ownedItemsByItemId,
|
||||||
|
IReadOnlyDictionary<int, int> gachaPointBalancesByPackId)
|
||||||
{
|
{
|
||||||
int openCount = openCounts.TryGetValue(p.Id, out var oc) ? oc.OpenCount : 0;
|
int openCount = openCounts.TryGetValue(p.Id, out var oc) ? oc.OpenCount : 0;
|
||||||
|
|
||||||
|
// Ticket-only pack: every child is TICKET (4) or TICKET_MULTI (5). These are
|
||||||
|
// gifted-currency packs (tutorial starter, throwback) that don't participate in
|
||||||
|
// gacha-point accrual or exchange, even if GachaPointConfig is set in seed.
|
||||||
|
bool isTicketOnly = p.ChildGachas.All(c => c.TypeDetail == 4 || c.TypeDetail == 5);
|
||||||
|
|
||||||
|
PackGachaPointDto? gachaPointDto = null;
|
||||||
|
if (p.GachaPointConfig is not null && !isTicketOnly)
|
||||||
|
{
|
||||||
|
int balance = gachaPointBalancesByPackId.TryGetValue(p.Id, out var b) ? b : 0;
|
||||||
|
int threshold = p.GachaPointConfig.ExchangeablePoint;
|
||||||
|
gachaPointDto = new PackGachaPointDto
|
||||||
|
{
|
||||||
|
PackId = p.BasePackId.ToString(CultureInfo.InvariantCulture),
|
||||||
|
GachaPoint = balance,
|
||||||
|
IncreaseGachaPoint = p.GachaPointConfig.IncreaseGachaPoint.ToString(CultureInfo.InvariantCulture),
|
||||||
|
ExchangeableGachaPoint = threshold,
|
||||||
|
IsExchangeableGachaPoint = balance >= threshold,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new PackConfigDto
|
return new PackConfigDto
|
||||||
{
|
{
|
||||||
ParentGachaId = p.Id,
|
ParentGachaId = p.Id,
|
||||||
@@ -133,14 +163,7 @@ public class PackController : SVSimController
|
|||||||
OpenCountLimit = p.OpenCountLimit,
|
OpenCountLimit = p.OpenCountLimit,
|
||||||
IsHide = p.IsHide ? 1 : 0,
|
IsHide = p.IsHide ? 1 : 0,
|
||||||
PackCategory = (int)p.PackCategory,
|
PackCategory = (int)p.PackCategory,
|
||||||
GachaPoint = p.GachaPointConfig is null ? null : new PackGachaPointDto
|
GachaPoint = gachaPointDto,
|
||||||
{
|
|
||||||
PackId = p.BasePackId.ToString(CultureInfo.InvariantCulture),
|
|
||||||
GachaPoint = 0,
|
|
||||||
IncreaseGachaPoint = p.GachaPointConfig.IncreaseGachaPoint.ToString(CultureInfo.InvariantCulture),
|
|
||||||
ExchangeableGachaPoint = p.GachaPointConfig.ExchangeablePoint,
|
|
||||||
IsExchangeableGachaPoint = false,
|
|
||||||
},
|
|
||||||
IsPreRelease = p.IsPreRelease,
|
IsPreRelease = p.IsPreRelease,
|
||||||
ExistsPurchaseReward = false,
|
ExistsPurchaseReward = false,
|
||||||
IsNew = p.IsNew,
|
IsNew = p.IsNew,
|
||||||
|
|||||||
@@ -130,4 +130,84 @@ public class PackControllerInfoTests
|
|||||||
Assert.That(gachaPoint.ValueKind, Is.EqualTo(JsonValueKind.Null),
|
Assert.That(gachaPoint.ValueKind, Is.EqualTo(JsonValueKind.Null),
|
||||||
"gacha_point should serialize as explicit null when no GachaPointConfig is set.");
|
"gacha_point should serialize as explicit null when no GachaPointConfig is set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Info_projects_viewer_gacha_point_balance_into_gacha_point_block()
|
||||||
|
{
|
||||||
|
using var factory = new SVSimTestFactory();
|
||||||
|
long viewerId = await factory.SeedViewerAsync();
|
||||||
|
using (var scope = factory.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||||
|
db.Packs.Add(new PackConfigEntry
|
||||||
|
{
|
||||||
|
Id = 10008, BasePackId = 10008, PackCategory = PackCategory.LegendCardPack,
|
||||||
|
CommenceDate = DateTime.UtcNow.AddDays(-1), CompleteDate = DateTime.UtcNow.AddDays(30),
|
||||||
|
GachaType = 1, GachaDetail = "test",
|
||||||
|
GachaPointConfig = new PackGachaPointConfig { ExchangeablePoint = 400, IncreaseGachaPoint = 1 },
|
||||||
|
ChildGachas =
|
||||||
|
{
|
||||||
|
// Must include at least one non-ticket child so this pack is NOT ticket-only
|
||||||
|
// and remains visible with a gacha_point block.
|
||||||
|
new PackChildGachaEntry { GachaId = 100087, TypeDetail = 7, Cost = 100, CardCount = 8 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var viewer = await db.Viewers
|
||||||
|
.Include(v => v.GachaPointBalances)
|
||||||
|
.FirstAsync(v => v.Id == viewerId);
|
||||||
|
viewer.GachaPointBalances.Add(new ViewerGachaPointBalance { PackId = 10008, Points = 450 });
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||||
|
var response = await client.PostAsync("/pack/info", JsonBody(EmptyEnvelope));
|
||||||
|
var text = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), text);
|
||||||
|
|
||||||
|
using var doc = JsonDocument.Parse(text);
|
||||||
|
var pack = doc.RootElement.GetProperty("pack_config_list")[0];
|
||||||
|
var gp = pack.GetProperty("gacha_point");
|
||||||
|
Assert.That(gp.GetProperty("gacha_point").GetInt32(), Is.EqualTo(450));
|
||||||
|
Assert.That(gp.GetProperty("is_exchangeable_gacha_point").GetBoolean(), Is.True,
|
||||||
|
"balance >= threshold should flip the gate");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Info_omits_gacha_point_block_for_ticket_only_packs()
|
||||||
|
{
|
||||||
|
using var factory = new SVSimTestFactory();
|
||||||
|
long viewerId = await factory.SeedViewerAsync();
|
||||||
|
using (var scope = factory.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||||
|
// Even though the pack has a GachaPointConfig, it must be hidden because every
|
||||||
|
// child is a ticket type (4 or 5). Mirrors prod for starter pack 99047.
|
||||||
|
db.Packs.Add(new PackConfigEntry
|
||||||
|
{
|
||||||
|
Id = 99047, BasePackId = 99047, PackCategory = PackCategory.LegendCardPack,
|
||||||
|
CommenceDate = DateTime.UtcNow.AddDays(-1), CompleteDate = DateTime.UtcNow.AddDays(30),
|
||||||
|
GachaType = 1, GachaDetail = "test",
|
||||||
|
GachaPointConfig = new PackGachaPointConfig { ExchangeablePoint = 400, IncreaseGachaPoint = 1 },
|
||||||
|
ChildGachas =
|
||||||
|
{
|
||||||
|
new PackChildGachaEntry { GachaId = 990475, TypeDetail = 5, Cost = 0, CardCount = 8 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
using var client = factory.CreateAuthenticatedClient(viewerId);
|
||||||
|
var response = await client.PostAsync("/pack/info", JsonBody(EmptyEnvelope));
|
||||||
|
var text = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), text);
|
||||||
|
|
||||||
|
using var doc = JsonDocument.Parse(text);
|
||||||
|
var pack = doc.RootElement.GetProperty("pack_config_list")[0];
|
||||||
|
// Either the key is absent (WhenWritingNull dropped it) or the value is null.
|
||||||
|
if (pack.TryGetProperty("gacha_point", out var gp))
|
||||||
|
{
|
||||||
|
Assert.That(gp.ValueKind, Is.EqualTo(JsonValueKind.Null),
|
||||||
|
"ticket-only pack must not emit a gacha_point block");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user