feat(leader-skin): shop catalog + 5 endpoints (/products, /buy, /buy_set, /buy_set_item, /ids)
Schema: LeaderSkinShopSeries -> Products (owned rewards) + owned SetCompletionRewards on the series; ViewerLeaderSkinSetClaim composite PK (ViewerId, SeriesId) backs the /buy_set_item idempotent-claim check. Importer mirrors SleeveShopImporter: idempotent find-or-create, owned collections rewritten wholesale on rerun. 16 series, 104 products. Controller (extends existing /set with 5 new endpoints): - /products: dict-keyed-by-series_id-string wire shape. is_completed per-viewer, rewards.status from ViewerLeaderSkinSetClaim (0=no set sale, 1=available, 2=claimed) matching client RewardStatus enum. - /buy: single skin, sales_type 1/2 dispatch, 3=>501. - /buy_set: whole series at SetPrice; requires set_sales_status != 0; grants every product's rewards (RewardGrantService idempotent on already-owned cosmetics, so partial-set buys don't double-add). - /buy_set_item: requires viewer owns every skin in series; idempotent on re-claim (returns 200 + empty reward_list, not 400) so client retries don't error. - /ids: flat owned-skin-id list for badge refresh. 496 tests pass (was 486; +10 leader-skin-shop tests). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,9 @@ public class SVSimDbContext : DbContext
|
||||
public DbSet<SleeveShopSeriesEntry> SleeveShopSeries => Set<SleeveShopSeriesEntry>();
|
||||
public DbSet<SleeveShopProductEntry> SleeveShopProducts => Set<SleeveShopProductEntry>();
|
||||
public DbSet<ItemPurchaseCatalogEntry> ItemPurchaseCatalog => Set<ItemPurchaseCatalogEntry>();
|
||||
public DbSet<LeaderSkinShopSeriesEntry> LeaderSkinShopSeries => Set<LeaderSkinShopSeriesEntry>();
|
||||
public DbSet<LeaderSkinShopProductEntry> LeaderSkinShopProducts => Set<LeaderSkinShopProductEntry>();
|
||||
public DbSet<ViewerLeaderSkinSetClaim> ViewerLeaderSkinSetClaims => Set<ViewerLeaderSkinSetClaim>();
|
||||
public DbSet<MaintenanceCardEntry> MaintenanceCards => Set<MaintenanceCardEntry>();
|
||||
public DbSet<FeatureMaintenanceEntry> FeatureMaintenances => Set<FeatureMaintenanceEntry>();
|
||||
public DbSet<PreReleaseInfo> PreReleaseInfos => Set<PreReleaseInfo>();
|
||||
@@ -191,6 +194,21 @@ public class SVSimDbContext : DbContext
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<SleeveShopProductEntry>().HasIndex(p => p.SeriesId);
|
||||
|
||||
modelBuilder.Entity<LeaderSkinShopSeriesEntry>().OwnsMany(s => s.SetCompletionRewards);
|
||||
modelBuilder.Entity<LeaderSkinShopProductEntry>().OwnsMany(p => p.Rewards);
|
||||
modelBuilder.Entity<LeaderSkinShopProductEntry>()
|
||||
.HasOne(p => p.Series)
|
||||
.WithMany(s => s.Products)
|
||||
.HasForeignKey(p => p.SeriesId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<LeaderSkinShopProductEntry>().HasIndex(p => p.SeriesId);
|
||||
|
||||
modelBuilder.Entity<ViewerLeaderSkinSetClaim>(b =>
|
||||
{
|
||||
b.HasKey(c => new { c.ViewerId, c.SeriesId });
|
||||
b.HasIndex(c => c.ViewerId);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<CardCosmeticReward>(b =>
|
||||
{
|
||||
b.HasKey(r => new { r.CardId, r.Type, r.CosmeticId });
|
||||
|
||||
Reference in New Issue
Block a user