Pack opening
This commit is contained in:
17
SVSim.Database/Enums/PackCategory.cs
Normal file
17
SVSim.Database/Enums/PackCategory.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace SVSim.Database.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors <c>Wizard.PackCategory</c> in the decompiled client
|
||||
/// (<c>Shadowverse_Code/Wizard/PackCategory.cs</c>). Wire value = (int)enum.
|
||||
/// </summary>
|
||||
public enum PackCategory
|
||||
{
|
||||
None = 0,
|
||||
LegendCardPack = 1,
|
||||
SpecialCardPack = 2,
|
||||
LimitedSpecialCardPack = 3,
|
||||
FreePackLeaderSkin = 4,
|
||||
RotationStarterCardPack = 5,
|
||||
LeaderSkinPack = 6,
|
||||
LegendAndLeaderSkinSinglePack = 7,
|
||||
}
|
||||
34843
SVSim.Database/Migrations/20260524044535_AddPackCatalog.Designer.cs
generated
Normal file
34843
SVSim.Database/Migrations/20260524044535_AddPackCatalog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
135
SVSim.Database/Migrations/20260524044535_AddPackCatalog.cs
Normal file
135
SVSim.Database/Migrations/20260524044535_AddPackCatalog.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SVSim.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPackCatalog : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Packs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false),
|
||||
BasePackId = table.Column<int>(type: "integer", nullable: false),
|
||||
GachaType = table.Column<int>(type: "integer", nullable: false),
|
||||
PackCategory = table.Column<int>(type: "integer", nullable: false),
|
||||
PosterType = table.Column<int>(type: "integer", nullable: false),
|
||||
CommenceDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
CompleteDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
SalesPeriodTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
SleeveId = table.Column<int>(type: "integer", nullable: false),
|
||||
SpecialSleeveId = table.Column<int>(type: "integer", nullable: false),
|
||||
OverrideDrawEffectPackId = table.Column<int>(type: "integer", nullable: false),
|
||||
OverrideUiEffectPackId = table.Column<int>(type: "integer", nullable: false),
|
||||
GachaDetail = table.Column<string>(type: "text", nullable: false),
|
||||
IsHide = table.Column<bool>(type: "boolean", nullable: false),
|
||||
IsNew = table.Column<bool>(type: "boolean", nullable: false),
|
||||
IsPreRelease = table.Column<bool>(type: "boolean", nullable: false),
|
||||
OpenCountLimit = table.Column<int>(type: "integer", nullable: false),
|
||||
GachaPointConfig_ExchangeablePoint = table.Column<int>(type: "integer", nullable: true),
|
||||
GachaPointConfig_IncreaseGachaPoint = table.Column<int>(type: "integer", 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_Packs", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ViewerPackOpenCount",
|
||||
columns: table => new
|
||||
{
|
||||
ViewerId = table.Column<long>(type: "bigint", nullable: false),
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
PackId = table.Column<int>(type: "integer", nullable: false),
|
||||
OpenCount = table.Column<int>(type: "integer", nullable: false),
|
||||
LastDailyFreeAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ViewerPackOpenCount", x => new { x.ViewerId, x.Id });
|
||||
table.ForeignKey(
|
||||
name: "FK_ViewerPackOpenCount_Viewers_ViewerId",
|
||||
column: x => x.ViewerId,
|
||||
principalTable: "Viewers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PackBannerEntry",
|
||||
columns: table => new
|
||||
{
|
||||
PackConfigEntryId = table.Column<int>(type: "integer", nullable: false),
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
BannerName = table.Column<string>(type: "text", nullable: false),
|
||||
DialogTitle = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PackBannerEntry", x => new { x.PackConfigEntryId, x.Id });
|
||||
table.ForeignKey(
|
||||
name: "FK_PackBannerEntry_Packs_PackConfigEntryId",
|
||||
column: x => x.PackConfigEntryId,
|
||||
principalTable: "Packs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PackChildGachaEntry",
|
||||
columns: table => new
|
||||
{
|
||||
PackConfigEntryId = table.Column<int>(type: "integer", nullable: false),
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
GachaId = table.Column<int>(type: "integer", nullable: false),
|
||||
TypeDetail = table.Column<int>(type: "integer", nullable: false),
|
||||
Cost = table.Column<int>(type: "integer", nullable: false),
|
||||
CardCount = table.Column<int>(type: "integer", nullable: false),
|
||||
ItemId = table.Column<long>(type: "bigint", nullable: true),
|
||||
IsDailySingle = table.Column<bool>(type: "boolean", nullable: false),
|
||||
OverrideIncreaseGachaPoint = table.Column<int>(type: "integer", nullable: false),
|
||||
PurchaseLimitCount = table.Column<int>(type: "integer", nullable: false),
|
||||
FreeGachaCampaignId = table.Column<int>(type: "integer", nullable: true),
|
||||
CampaignName = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PackChildGachaEntry", x => new { x.PackConfigEntryId, x.Id });
|
||||
table.ForeignKey(
|
||||
name: "FK_PackChildGachaEntry_Packs_PackConfigEntryId",
|
||||
column: x => x.PackConfigEntryId,
|
||||
principalTable: "Packs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "PackBannerEntry");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PackChildGachaEntry");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ViewerPackOpenCount");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Packs");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25422,6 +25422,71 @@ namespace SVSim.Database.Migrations
|
||||
b.ToTable("MyRotationSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.PackConfigEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("BasePackId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("CommenceDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("CompleteDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DateUpdated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("GachaDetail")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("GachaType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("IsHide")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsNew")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsPreRelease")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("OpenCountLimit")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("OverrideDrawEffectPackId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("OverrideUiEffectPackId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PackCategory")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PosterType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("SalesPeriodTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("SleeveId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("SpecialSleeveId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Packs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.PaymentItemEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -34209,6 +34274,110 @@ namespace SVSim.Database.Migrations
|
||||
b.Navigation("Class");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.PackConfigEntry", b =>
|
||||
{
|
||||
b.OwnsMany("SVSim.Database.Models.PackBannerEntry", "Banners", b1 =>
|
||||
{
|
||||
b1.Property<int>("PackConfigEntryId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<int>("Id"));
|
||||
|
||||
b1.Property<string>("BannerName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b1.Property<string>("DialogTitle")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b1.HasKey("PackConfigEntryId", "Id");
|
||||
|
||||
b1.ToTable("PackBannerEntry");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PackConfigEntryId");
|
||||
});
|
||||
|
||||
b.OwnsMany("SVSim.Database.Models.PackChildGachaEntry", "ChildGachas", b1 =>
|
||||
{
|
||||
b1.Property<int>("PackConfigEntryId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<int>("Id"));
|
||||
|
||||
b1.Property<string>("CampaignName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b1.Property<int>("CardCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("Cost")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int?>("FreeGachaCampaignId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("GachaId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<bool>("IsDailySingle")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b1.Property<long?>("ItemId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b1.Property<int>("OverrideIncreaseGachaPoint")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("PurchaseLimitCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("TypeDetail")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.HasKey("PackConfigEntryId", "Id");
|
||||
|
||||
b1.ToTable("PackChildGachaEntry");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PackConfigEntryId");
|
||||
});
|
||||
|
||||
b.OwnsOne("SVSim.Database.Models.PackGachaPointConfig", "GachaPointConfig", b1 =>
|
||||
{
|
||||
b1.Property<int>("PackConfigEntryId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("ExchangeablePoint")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("IncreaseGachaPoint")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.HasKey("PackConfigEntryId");
|
||||
|
||||
b1.ToTable("Packs");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PackConfigEntryId");
|
||||
});
|
||||
|
||||
b.Navigation("Banners");
|
||||
|
||||
b.Navigation("ChildGachas");
|
||||
|
||||
b.Navigation("GachaPointConfig");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseCardEntry", b =>
|
||||
{
|
||||
b.HasOne("SVSim.Database.Models.ClassEntry", "Class")
|
||||
@@ -34588,6 +34757,34 @@ namespace SVSim.Database.Migrations
|
||||
.HasForeignKey("ViewerId");
|
||||
});
|
||||
|
||||
b.OwnsMany("SVSim.Database.Models.ViewerPackOpenCount", "PackOpenCounts", b1 =>
|
||||
{
|
||||
b1.Property<long>("ViewerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b1.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<int>("Id"));
|
||||
|
||||
b1.Property<DateTime?>("LastDailyFreeAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b1.Property<int>("OpenCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("PackId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.HasKey("ViewerId", "Id");
|
||||
|
||||
b1.ToTable("ViewerPackOpenCount");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ViewerId");
|
||||
});
|
||||
|
||||
b.Navigation("Cards");
|
||||
|
||||
b.Navigation("Classes");
|
||||
@@ -34603,6 +34800,8 @@ namespace SVSim.Database.Migrations
|
||||
b.Navigation("MissionData")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("PackOpenCounts");
|
||||
|
||||
b.Navigation("SocialAccountConnections");
|
||||
});
|
||||
|
||||
|
||||
11
SVSim.Database/Models/PackBannerEntry.cs
Normal file
11
SVSim.Database/Models/PackBannerEntry.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>One entry of <c>cardpack_banner_list</c> in /pack/info. Owned by PackConfigEntry.</summary>
|
||||
[Owned]
|
||||
public class PackBannerEntry
|
||||
{
|
||||
public string BannerName { get; set; } = string.Empty;
|
||||
public string DialogTitle { get; set; } = string.Empty;
|
||||
}
|
||||
26
SVSim.Database/Models/PackChildGachaEntry.cs
Normal file
26
SVSim.Database/Models/PackChildGachaEntry.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>
|
||||
/// One sub-option inside a pack (single-open / 10-open / ticket / daily-free).
|
||||
/// Wire shape: one entry of <c>child_gacha_info</c> in /pack/info. Owned by PackConfigEntry.
|
||||
/// <c>TypeDetail</c> corresponds to <c>GachaUI.CardPackType</c>:
|
||||
/// 1=CRYSTAL, 2=CRYSTAL_MULTI, 3=DAILY, 4=TICKET, 5=TICKET_MULTI, 6=RUPY, 7=RUPY_MULTI,
|
||||
/// 8=CRYSTAL_SPECIAL, 9=CRYSTAL_SELECT_SKIN, 10=FREE_PACKS, 11=FREE_PACK_WITH_SKIN,
|
||||
/// 12=ROTATION_STARTER_PACK, 13=CRYSTAL_ACQUIRE_SKIN_CARD_PACK.
|
||||
/// </summary>
|
||||
[Owned]
|
||||
public class PackChildGachaEntry
|
||||
{
|
||||
public int GachaId { get; set; }
|
||||
public int TypeDetail { get; set; }
|
||||
public int Cost { get; set; }
|
||||
public int CardCount { get; set; }
|
||||
public long? ItemId { get; set; }
|
||||
public bool IsDailySingle { get; set; }
|
||||
public int OverrideIncreaseGachaPoint { get; set; }
|
||||
public int PurchaseLimitCount { get; set; }
|
||||
public int? FreeGachaCampaignId { get; set; }
|
||||
public string? CampaignName { get; set; }
|
||||
}
|
||||
40
SVSim.Database/Models/PackConfigEntry.cs
Normal file
40
SVSim.Database/Models/PackConfigEntry.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using SVSim.Database.Common;
|
||||
using SVSim.Database.Enums;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>
|
||||
/// One row of /pack/info's <c>pack_config_list</c>. PK = <c>parent_gacha_id</c> (the wire id the
|
||||
/// client treats as "this pack"). Child gachas and banners are owned collections — replaced
|
||||
/// wholesale on importer re-runs.
|
||||
/// </summary>
|
||||
public class PackConfigEntry : BaseEntity<int>
|
||||
{
|
||||
public int BasePackId { get; set; }
|
||||
public int GachaType { get; set; }
|
||||
public PackCategory PackCategory { get; set; }
|
||||
public int PosterType { get; set; }
|
||||
|
||||
public DateTime CommenceDate { get; set; }
|
||||
public DateTime CompleteDate { get; set; }
|
||||
public DateTime? SalesPeriodTime { get; set; }
|
||||
|
||||
public int SleeveId { get; set; }
|
||||
public int SpecialSleeveId { get; set; }
|
||||
|
||||
public int OverrideDrawEffectPackId { get; set; }
|
||||
public int OverrideUiEffectPackId { get; set; }
|
||||
|
||||
public string GachaDetail { get; set; } = string.Empty;
|
||||
|
||||
public bool IsHide { get; set; }
|
||||
public bool IsNew { get; set; }
|
||||
public bool IsPreRelease { get; set; }
|
||||
|
||||
public int OpenCountLimit { get; set; }
|
||||
|
||||
public PackGachaPointConfig? GachaPointConfig { get; set; }
|
||||
|
||||
public List<PackBannerEntry> Banners { get; set; } = new();
|
||||
public List<PackChildGachaEntry> ChildGachas { get; set; } = new();
|
||||
}
|
||||
16
SVSim.Database/Models/PackGachaPointConfig.cs
Normal file
16
SVSim.Database/Models/PackGachaPointConfig.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Per-pack gacha-point exchange config. Owned by <see cref="PackConfigEntry"/>; null when the
|
||||
/// pack does not participate in gacha-point exchange. Wire shape (from /pack/info):
|
||||
/// <c>{"pack_id":"10001","gacha_point":0,"increase_gacha_point":"1","exchangeable_gacha_point":400,"is_exchangeable_gacha_point":false}</c>.
|
||||
/// v1 only persists the static catalog values; per-viewer accrual is deferred.
|
||||
/// </summary>
|
||||
[Owned]
|
||||
public class PackGachaPointConfig
|
||||
{
|
||||
public int ExchangeablePoint { get; set; }
|
||||
public int IncreaseGachaPoint { get; set; }
|
||||
}
|
||||
@@ -55,6 +55,8 @@ public class Viewer : BaseEntity<long>
|
||||
|
||||
public List<MyPageBackgroundEntry> MyPageBackgrounds { get; set; } = new List<MyPageBackgroundEntry>();
|
||||
|
||||
public List<ViewerPackOpenCount> PackOpenCounts { get; set; } = new List<ViewerPackOpenCount>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Navigation Properties
|
||||
|
||||
17
SVSim.Database/Models/ViewerPackOpenCount.cs
Normal file
17
SVSim.Database/Models/ViewerPackOpenCount.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Per-viewer, per-pack open counter. Owned collection on <see cref="Viewer"/>.
|
||||
/// <c>PackId</c> = parent_gacha_id. <c>LastDailyFreeAt</c> is null until the viewer first opens
|
||||
/// a DAILY (type_detail=3) child gacha; thereafter the controller compares it against the daily
|
||||
/// reset boundary to decide whether the free open is available again.
|
||||
/// </summary>
|
||||
[Owned]
|
||||
public class ViewerPackOpenCount
|
||||
{
|
||||
public int PackId { get; set; }
|
||||
public int OpenCount { get; set; }
|
||||
public DateTime? LastDailyFreeAt { get; set; }
|
||||
}
|
||||
13
SVSim.Database/Repositories/Pack/IPackRepository.cs
Normal file
13
SVSim.Database/Repositories/Pack/IPackRepository.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using SVSim.Database.Models;
|
||||
|
||||
namespace SVSim.Database.Repositories.Pack;
|
||||
|
||||
public interface IPackRepository
|
||||
{
|
||||
Task<List<PackConfigEntry>> GetActivePacks(DateTime now);
|
||||
Task<PackConfigEntry?> GetPack(int parentGachaId);
|
||||
Task<Dictionary<int, ViewerPackOpenCount>> GetOpenCountsForViewer(long viewerId);
|
||||
Task IncrementOpenCount(long viewerId, int parentGachaId, int by);
|
||||
Task MarkDailyFreeUsed(long viewerId, int parentGachaId, DateTime when);
|
||||
Task GrantCardsToViewer(long viewerId, IEnumerable<long> cardIds);
|
||||
}
|
||||
90
SVSim.Database/Repositories/Pack/PackRepository.cs
Normal file
90
SVSim.Database/Repositories/Pack/PackRepository.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database.Models;
|
||||
|
||||
namespace SVSim.Database.Repositories.Pack;
|
||||
|
||||
public class PackRepository : IPackRepository
|
||||
{
|
||||
private readonly SVSimDbContext _db;
|
||||
public PackRepository(SVSimDbContext db) { _db = db; }
|
||||
|
||||
public async Task<List<PackConfigEntry>> GetActivePacks(DateTime now) =>
|
||||
await _db.Packs
|
||||
.Include(p => p.ChildGachas)
|
||||
.Include(p => p.Banners)
|
||||
.Where(p => p.CommenceDate <= now && p.CompleteDate >= now)
|
||||
.ToListAsync();
|
||||
|
||||
public async Task<PackConfigEntry?> GetPack(int parentGachaId) =>
|
||||
await _db.Packs
|
||||
.Include(p => p.ChildGachas)
|
||||
.Include(p => p.Banners)
|
||||
.FirstOrDefaultAsync(p => p.Id == parentGachaId);
|
||||
|
||||
public async Task<Dictionary<int, ViewerPackOpenCount>> GetOpenCountsForViewer(long viewerId)
|
||||
{
|
||||
var viewer = await _db.Viewers
|
||||
.Include(v => v.PackOpenCounts)
|
||||
.FirstOrDefaultAsync(v => v.Id == viewerId);
|
||||
return viewer?.PackOpenCounts.ToDictionary(p => p.PackId) ?? new();
|
||||
}
|
||||
|
||||
public async Task IncrementOpenCount(long viewerId, int parentGachaId, int by)
|
||||
{
|
||||
var viewer = await _db.Viewers
|
||||
.Include(v => v.PackOpenCounts)
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
var row = viewer.PackOpenCounts.FirstOrDefault(p => p.PackId == parentGachaId);
|
||||
if (row is null)
|
||||
{
|
||||
viewer.PackOpenCounts.Add(new ViewerPackOpenCount { PackId = parentGachaId, OpenCount = by });
|
||||
}
|
||||
else
|
||||
{
|
||||
row.OpenCount += by;
|
||||
}
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task MarkDailyFreeUsed(long viewerId, int parentGachaId, DateTime when)
|
||||
{
|
||||
var viewer = await _db.Viewers
|
||||
.Include(v => v.PackOpenCounts)
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
var row = viewer.PackOpenCounts.FirstOrDefault(p => p.PackId == parentGachaId);
|
||||
if (row is null)
|
||||
{
|
||||
viewer.PackOpenCounts.Add(new ViewerPackOpenCount { PackId = parentGachaId, OpenCount = 0, LastDailyFreeAt = when });
|
||||
}
|
||||
else
|
||||
{
|
||||
row.LastDailyFreeAt = when;
|
||||
}
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task GrantCardsToViewer(long viewerId, IEnumerable<long> cardIds)
|
||||
{
|
||||
var viewer = await _db.Viewers
|
||||
.Include(v => v.Cards).ThenInclude(c => c.Card)
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
|
||||
var byId = viewer.Cards.ToDictionary(c => c.Card.Id);
|
||||
foreach (var grp in cardIds.GroupBy(id => id))
|
||||
{
|
||||
if (byId.TryGetValue(grp.Key, out var existing))
|
||||
{
|
||||
existing.Count += grp.Count();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Look up the card by id and attach it so we don't insert a phantom Card row.
|
||||
var card = await _db.Cards.FirstAsync(c => c.Id == grp.Key);
|
||||
var owned = new OwnedCardEntry { Card = card, Count = grp.Count(), IsProtected = false };
|
||||
viewer.Cards.Add(owned);
|
||||
byId[grp.Key] = owned;
|
||||
}
|
||||
}
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ public class SVSimDbContext : DbContext
|
||||
public DbSet<MasterPointRankingPeriodEntry> MasterPointRankingPeriods => Set<MasterPointRankingPeriodEntry>();
|
||||
public DbSet<SpecialDeckFormatEntry> SpecialDeckFormats => Set<SpecialDeckFormatEntry>();
|
||||
public DbSet<PaymentItemEntry> PaymentItems => Set<PaymentItemEntry>();
|
||||
public DbSet<PackConfigEntry> Packs => Set<PackConfigEntry>();
|
||||
public DbSet<MaintenanceCardEntry> MaintenanceCards => Set<MaintenanceCardEntry>();
|
||||
public DbSet<FeatureMaintenanceEntry> FeatureMaintenances => Set<FeatureMaintenanceEntry>();
|
||||
public DbSet<PreReleaseInfo> PreReleaseInfos => Set<PreReleaseInfo>();
|
||||
@@ -107,6 +108,10 @@ public class SVSimDbContext : DbContext
|
||||
.Property(v => v.ShortUdid)
|
||||
.UseSequence("ShortUdidSequence");
|
||||
|
||||
modelBuilder.Entity<PackConfigEntry>().OwnsMany(p => p.ChildGachas);
|
||||
modelBuilder.Entity<PackConfigEntry>().OwnsMany(p => p.Banners);
|
||||
modelBuilder.Entity<Viewer>().OwnsMany(v => v.PackOpenCounts);
|
||||
|
||||
new BaseDataSeeder().Seed(modelBuilder);
|
||||
new DefaultSettingsSeeder().Seed(modelBuilder);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user