Deck list work
This commit is contained in:
34529
SVSim.Database/Migrations/20260523225845_MypageRoomTypeInSession.Designer.cs
generated
Normal file
34529
SVSim.Database/Migrations/20260523225845_MypageRoomTypeInSession.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SVSim.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MypageRoomTypeInSession : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SpecialDeckFormats",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false),
|
||||
DeckFormat = table.Column<string>(type: "text", nullable: false),
|
||||
EndTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SpecialDeckFormats", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "SpecialDeckFormats");
|
||||
}
|
||||
}
|
||||
}
|
||||
34593
SVSim.Database/Migrations/20260523232258_MypagePaymentItems.Designer.cs
generated
Normal file
34593
SVSim.Database/Migrations/20260523232258_MypagePaymentItems.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SVSim.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MypagePaymentItems : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PaymentItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false),
|
||||
ProductId = table.Column<int>(type: "integer", nullable: false),
|
||||
StoreProductId = table.Column<long>(type: "bigint", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Text = table.Column<string>(type: "text", nullable: false),
|
||||
Price = table.Column<decimal>(type: "numeric", nullable: false),
|
||||
ChargeCrystalNum = table.Column<int>(type: "integer", nullable: false),
|
||||
FreeCrystalNum = table.Column<int>(type: "integer", nullable: false),
|
||||
PurchaseLimit = table.Column<int>(type: "integer", nullable: false),
|
||||
SpecialShopFlag = table.Column<int>(type: "integer", nullable: false),
|
||||
ImageName = table.Column<string>(type: "text", nullable: false),
|
||||
StartTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
EndTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
RemainingTime = table.Column<int>(type: "integer", nullable: false),
|
||||
IsResaleProduct = table.Column<int>(type: "integer", nullable: false),
|
||||
ResaleStartDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PaymentItems", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "PaymentItems");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25422,6 +25422,70 @@ namespace SVSim.Database.Migrations
|
||||
b.ToTable("MyRotationSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.PaymentItemEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ChargeCrystalNum")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DateUpdated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("EndTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("FreeCrystalNum")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ImageName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("IsResaleProduct")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<int>("ProductId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PurchaseLimit")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("RemainingTime")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("ResaleStartDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("SpecialShopFlag")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("StartTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("StoreProductId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PaymentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.PreReleaseInfo", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -33873,6 +33937,29 @@ namespace SVSim.Database.Migrations
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.SpecialDeckFormatEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DateUpdated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("DeckFormat")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("EndTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SpecialDeckFormats");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.SpotCardEntry", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
|
||||
51
SVSim.Database/Models/PaymentItemEntry.cs
Normal file
51
SVSim.Database/Models/PaymentItemEntry.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>
|
||||
/// One row of the Steam/PC storefront item list from /payment_pc/item_list data. Singleton per
|
||||
/// product. Id is the wire's <c>record_id</c> (prod's auto-increment, genuinely stable across
|
||||
/// captures — same upsert-by-wire-id pattern as MasterPointRankingPeriodEntry, not the synthetic-
|
||||
/// ordinal Banner pattern).
|
||||
///
|
||||
/// All numeric fields land in typed columns; the controller stringifies them on the way out to
|
||||
/// match prod's PHP-stringified wire convention.
|
||||
/// </summary>
|
||||
public class PaymentItemEntry : BaseEntity<int>
|
||||
{
|
||||
/// <summary>Internal product id (different from store_product_id). Used by the client at
|
||||
/// PaymentItemListTask.cs:50,58,64,67 as a per-tier discriminator.</summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>User-visible SKU (e.g. 10011 for "60-crystal set"). Wire dict key.</summary>
|
||||
public long StoreProductId { get; set; }
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
public decimal Price { get; set; }
|
||||
|
||||
public int ChargeCrystalNum { get; set; }
|
||||
|
||||
public int FreeCrystalNum { get; set; }
|
||||
|
||||
public int PurchaseLimit { get; set; }
|
||||
|
||||
/// <summary>0/1 — special_shop_flag on the wire (stringified).</summary>
|
||||
public int SpecialShopFlag { get; set; }
|
||||
|
||||
public string ImageName { get; set; } = string.Empty;
|
||||
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
public DateTime EndTime { get; set; }
|
||||
|
||||
public int RemainingTime { get; set; }
|
||||
|
||||
/// <summary>0/1 — is_resale_product on the wire (stringified).</summary>
|
||||
public int IsResaleProduct { get; set; }
|
||||
|
||||
/// <summary>Nullable — prod sends empty string when unset; we store null and emit "".</summary>
|
||||
public DateTime? ResaleStartDate { get; set; }
|
||||
}
|
||||
17
SVSim.Database/Models/SpecialDeckFormatEntry.cs
Normal file
17
SVSim.Database/Models/SpecialDeckFormatEntry.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>
|
||||
/// One entry from /mypage/index data.room_type_in_session.special_deck_format_list. A list of deck-format
|
||||
/// codes that have a current "special" window (e.g. format "5" valid until 2030-06-26). Id is a synthetic
|
||||
/// ordinal — the wire has no explicit identifier, and ImportRoomTypeInSession follows the same clear-and-
|
||||
/// rewrite pattern as ImportBanners.
|
||||
/// </summary>
|
||||
public class SpecialDeckFormatEntry : BaseEntity<int>
|
||||
{
|
||||
/// <summary>Wire is string per prod's PHP convention even though it looks numeric (e.g. "5").</summary>
|
||||
public string DeckFormat { get; set; } = string.Empty;
|
||||
|
||||
public DateTime EndTime { get; set; }
|
||||
}
|
||||
@@ -28,6 +28,27 @@ public class DeckRepository : IDeckRepository
|
||||
?? new List<ShadowverseDeckEntry>();
|
||||
}
|
||||
|
||||
public async Task<Dictionary<Format, List<ShadowverseDeckEntry>>> GetDecksByFormats(long viewerId, IEnumerable<Format> formats)
|
||||
{
|
||||
var requested = formats.ToHashSet();
|
||||
var viewer = await _dbContext.Viewers
|
||||
.AsNoTracking()
|
||||
.Include(v => v.Decks).ThenInclude(d => d.Class)
|
||||
.Include(v => v.Decks).ThenInclude(d => d.Sleeve)
|
||||
.Include(v => v.Decks).ThenInclude(d => d.LeaderSkin)
|
||||
.FirstOrDefaultAsync(v => v.Id == viewerId);
|
||||
|
||||
// Seed every requested format with an empty list so callers iterate without null checks.
|
||||
var result = requested.ToDictionary(f => f, _ => new List<ShadowverseDeckEntry>());
|
||||
if (viewer is null) return result;
|
||||
|
||||
foreach (var deck in viewer.Decks.Where(d => requested.Contains(d.Format)).OrderBy(d => d.Number))
|
||||
{
|
||||
result[deck.Format].Add(deck);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<ShadowverseDeckEntry?> GetDeck(long viewerId, Format format, int deckNo)
|
||||
{
|
||||
var viewer = await _dbContext.Viewers
|
||||
|
||||
@@ -6,6 +6,14 @@ namespace SVSim.Database.Repositories.Deck;
|
||||
public interface IDeckRepository
|
||||
{
|
||||
Task<List<ShadowverseDeckEntry>> GetDecks(long viewerId, Format format);
|
||||
|
||||
/// <summary>
|
||||
/// Bulk-fetch viewer decks grouped by format. Returns a dict keyed by every format in
|
||||
/// <paramref name="formats"/> — missing formats map to empty lists so callers don't need
|
||||
/// dict-existence checks. Single viewer-load, no N+1.
|
||||
/// </summary>
|
||||
Task<Dictionary<Format, List<ShadowverseDeckEntry>>> GetDecksByFormats(long viewerId, IEnumerable<Format> formats);
|
||||
|
||||
Task<ShadowverseDeckEntry?> GetDeck(long viewerId, Format format, int deckNo);
|
||||
Task<int> GetEmptyDeckNumber(long viewerId, Format format);
|
||||
Task<ShadowverseDeckEntry> UpsertDeck(long viewerId, Format format, int deckNo, Action<ShadowverseDeckEntry> mutate);
|
||||
|
||||
@@ -94,6 +94,12 @@ public class GlobalsRepository : IGlobalsRepository
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public Task<List<SpecialDeckFormatEntry>> GetActiveSpecialDeckFormats() =>
|
||||
_dbContext.SpecialDeckFormats.AsNoTracking().OrderBy(e => e.Id).ToListAsync();
|
||||
|
||||
public Task<List<PaymentItemEntry>> GetPaymentItems() =>
|
||||
_dbContext.PaymentItems.AsNoTracking().OrderBy(e => e.Id).ToListAsync();
|
||||
|
||||
public Task<List<MaintenanceCardEntry>> GetMaintenanceCards() =>
|
||||
_dbContext.MaintenanceCards.AsNoTracking().ToListAsync();
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public interface IGlobalsRepository
|
||||
Task<ColosseumConfig?> GetCurrentColosseum();
|
||||
Task<SealedConfig?> GetCurrentSealedSeason();
|
||||
Task<MasterPointRankingPeriodEntry?> GetCurrentMasterPointPeriod();
|
||||
Task<List<SpecialDeckFormatEntry>> GetActiveSpecialDeckFormats();
|
||||
Task<List<PaymentItemEntry>> GetPaymentItems();
|
||||
Task<List<MaintenanceCardEntry>> GetMaintenanceCards();
|
||||
Task<List<FeatureMaintenanceEntry>> GetFeatureMaintenances();
|
||||
Task<PreReleaseInfo?> GetPreReleaseInfo();
|
||||
|
||||
@@ -54,6 +54,8 @@ public class SVSimDbContext : DbContext
|
||||
public DbSet<ColosseumConfig> Colosseums => Set<ColosseumConfig>();
|
||||
public DbSet<SealedConfig> SealedSeasons => Set<SealedConfig>();
|
||||
public DbSet<MasterPointRankingPeriodEntry> MasterPointRankingPeriods => Set<MasterPointRankingPeriodEntry>();
|
||||
public DbSet<SpecialDeckFormatEntry> SpecialDeckFormats => Set<SpecialDeckFormatEntry>();
|
||||
public DbSet<PaymentItemEntry> PaymentItems => Set<PaymentItemEntry>();
|
||||
public DbSet<MaintenanceCardEntry> MaintenanceCards => Set<MaintenanceCardEntry>();
|
||||
public DbSet<FeatureMaintenanceEntry> FeatureMaintenances => Set<FeatureMaintenanceEntry>();
|
||||
public DbSet<PreReleaseInfo> PreReleaseInfos => Set<PreReleaseInfo>();
|
||||
|
||||
Reference in New Issue
Block a user