diff --git a/src/Giveaways.Data/AppDbContext.cs b/src/Giveaways.Data/AppDbContext.cs index 0c02f52..ecad024 100644 --- a/src/Giveaways.Data/AppDbContext.cs +++ b/src/Giveaways.Data/AppDbContext.cs @@ -11,6 +11,6 @@ namespace Giveaways.Data; /// The options for this context. public class AppDbContext(DbContextOptions options) : DbContext(options) { - // TODO: Configure the database context. - // Learn more at the https://learn.microsoft.com/ef/core. + public DbSet Giveaways { get; set; } + public DbSet GiveawayParticipants { get; set; } } diff --git a/src/Giveaways.Data/Migrations/20240624181527_InitialCreate.Designer.cs b/src/Giveaways.Data/Migrations/20240624181527_InitialCreate.Designer.cs new file mode 100644 index 0000000..51b0911 --- /dev/null +++ b/src/Giveaways.Data/Migrations/20240624181527_InitialCreate.Designer.cs @@ -0,0 +1,88 @@ +// +using System; +using Giveaways.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Giveaways.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20240624181527_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Giveaways.Data.Giveaway", b => + { + b.Property("MessageId") + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("GuildId") + .HasColumnType("INTEGER"); + + b.Property("MaxWinners") + .HasColumnType("INTEGER"); + + b.Property("Prize") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("MessageId"); + + b.ToTable("Giveaways"); + }); + + modelBuilder.Entity("Giveaways.Data.GiveawayParticipant", b => + { + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("GiveawayId") + .HasColumnType("INTEGER"); + + b.Property("IsWinner") + .HasColumnType("INTEGER"); + + b.HasKey("UserId", "GiveawayId"); + + b.HasIndex("GiveawayId"); + + b.ToTable("GiveawayParticipants"); + }); + + modelBuilder.Entity("Giveaways.Data.GiveawayParticipant", b => + { + b.HasOne("Giveaways.Data.Giveaway", "Giveaway") + .WithMany("Participants") + .HasForeignKey("GiveawayId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Giveaway"); + }); + + modelBuilder.Entity("Giveaways.Data.Giveaway", b => + { + b.Navigation("Participants"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Giveaways.Data/Migrations/20240624181527_InitialCreate.cs b/src/Giveaways.Data/Migrations/20240624181527_InitialCreate.cs new file mode 100644 index 0000000..b44cf53 --- /dev/null +++ b/src/Giveaways.Data/Migrations/20240624181527_InitialCreate.cs @@ -0,0 +1,66 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Giveaways.Data.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Giveaways", + columns: table => new + { + MessageId = table.Column(type: "INTEGER", nullable: false), + ChannelId = table.Column(type: "INTEGER", nullable: false), + GuildId = table.Column(type: "INTEGER", nullable: false), + Prize = table.Column(type: "TEXT", nullable: false), + MaxWinners = table.Column(type: "INTEGER", nullable: false), + ExpiresAt = table.Column(type: "TEXT", nullable: false), + Status = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Giveaways", x => x.MessageId); + }); + + migrationBuilder.CreateTable( + name: "GiveawayParticipants", + columns: table => new + { + UserId = table.Column(type: "INTEGER", nullable: false), + GiveawayId = table.Column(type: "INTEGER", nullable: false), + IsWinner = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GiveawayParticipants", x => new { x.UserId, x.GiveawayId }); + table.ForeignKey( + name: "FK_GiveawayParticipants_Giveaways_GiveawayId", + column: x => x.GiveawayId, + principalTable: "Giveaways", + principalColumn: "MessageId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_GiveawayParticipants_GiveawayId", + table: "GiveawayParticipants", + column: "GiveawayId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "GiveawayParticipants"); + + migrationBuilder.DropTable( + name: "Giveaways"); + } + } +} diff --git a/src/Giveaways.Data/Migrations/AppDbContextModelSnapshot.cs b/src/Giveaways.Data/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000..4c81856 --- /dev/null +++ b/src/Giveaways.Data/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,85 @@ +// +using System; +using Giveaways.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Giveaways.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Giveaways.Data.Giveaway", b => + { + b.Property("MessageId") + .HasColumnType("INTEGER"); + + b.Property("ChannelId") + .HasColumnType("INTEGER"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("GuildId") + .HasColumnType("INTEGER"); + + b.Property("MaxWinners") + .HasColumnType("INTEGER"); + + b.Property("Prize") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("MessageId"); + + b.ToTable("Giveaways"); + }); + + modelBuilder.Entity("Giveaways.Data.GiveawayParticipant", b => + { + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("GiveawayId") + .HasColumnType("INTEGER"); + + b.Property("IsWinner") + .HasColumnType("INTEGER"); + + b.HasKey("UserId", "GiveawayId"); + + b.HasIndex("GiveawayId"); + + b.ToTable("GiveawayParticipants"); + }); + + modelBuilder.Entity("Giveaways.Data.GiveawayParticipant", b => + { + b.HasOne("Giveaways.Data.Giveaway", "Giveaway") + .WithMany("Participants") + .HasForeignKey("GiveawayId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Giveaway"); + }); + + modelBuilder.Entity("Giveaways.Data.Giveaway", b => + { + b.Navigation("Participants"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Giveaways.Data/Models/Giveaway.cs b/src/Giveaways.Data/Models/Giveaway.cs new file mode 100644 index 0000000..0f32d34 --- /dev/null +++ b/src/Giveaways.Data/Models/Giveaway.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Giveaways.Data; + +/// +/// Represents a giveaway hosted by a user. +/// +public class Giveaway +{ + /// + /// Gets the message identifier of the giveaway message. + /// + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public required ulong MessageId { get; init; } + + /// + /// Gets the channel identifier where this giveaway was hosted. + /// + public required ulong ChannelId { get; init; } + + /// + /// Gets the guild identifier where this giveaway was hosted. + /// + public required ulong GuildId { get; init; } + + /// + /// Gets or sets the prize of this giveaway. + /// + public required string Prize { get; set; } + + /// + /// Gets or sets the maximum number of winners for this giveaway. + /// + public required int MaxWinners { get; set; } + + /// + /// Gets or sets the expiration date and time for this giveaway. + /// + public required DateTime ExpiresAt { get; set; } + + /// + /// Gets or sets the current status of this giveaway. + /// + public GiveawayStatus Status { get; set; } = GiveawayStatus.Active; + + /// + /// Gets the list of participants for this giveaway. + /// + public List Participants { get; } = []; +} diff --git a/src/Giveaways.Data/Models/GiveawayParticipant.cs b/src/Giveaways.Data/Models/GiveawayParticipant.cs new file mode 100644 index 0000000..7fae017 --- /dev/null +++ b/src/Giveaways.Data/Models/GiveawayParticipant.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Giveaways.Data; + +/// +/// Represents a participant of a giveaway. +/// +[PrimaryKey(nameof(UserId), nameof(GiveawayId))] +public class GiveawayParticipant +{ + /// + /// Gets or sets the user ID of the participant. + /// + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public ulong UserId { get; set; } + + /// + /// Gets or sets a value indicating whether the participant is a winner. + /// + public bool IsWinner { get; set; } = false; + + /// + /// Gets or sets the ID of the giveaway the participant is associated with. + /// + public ulong GiveawayId { get; set; } + + /// + /// Gets or sets the reference to the giveaway the participant is associated with. + /// + public Giveaway Giveaway { get; set; } = null!; +} diff --git a/src/Giveaways.Data/Models/GiveawayStatus.cs b/src/Giveaways.Data/Models/GiveawayStatus.cs new file mode 100644 index 0000000..afd69fc --- /dev/null +++ b/src/Giveaways.Data/Models/GiveawayStatus.cs @@ -0,0 +1,22 @@ +namespace Giveaways; + +/// +/// Represents the status of a giveaway. +/// +public enum GiveawayStatus +{ + /// + /// The giveaway is currently active and participants can still enter. + /// + Active, + + /// + /// The giveaway is temporarily suspended and not accepting new entries. + /// + Suspended, + + /// + /// The giveaway has ended, and winners have been selected. + /// + Ended, +}