feat:Initial commit

This commit is contained in:
elar1s
2025-09-25 22:21:41 +03:00
commit 02934b1fd9
35 changed files with 1267 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
using Microsoft.EntityFrameworkCore;
using StoreService.Database.Entities;
namespace StoreService.Database;
/// <summary>
/// Entity Framework Core database context for the Store microservice.
/// Defines DbSets and configures entity relationships & constraints.
/// </summary>
public class ApplicationContext : DbContext
{
#region Ctor
public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options)
{
}
#endregion
#region DbSets
public DbSet<StoreCategory> StoreCategories => Set<StoreCategory>();
public DbSet<StoreItem> StoreItems => Set<StoreItem>();
public DbSet<StoreDiscount> StoreDiscounts => Set<StoreDiscount>();
public DbSet<StoreDiscountItem> StoreDiscountItems => Set<StoreDiscountItem>();
public DbSet<StoreOrder> StoreOrders => Set<StoreOrder>();
public DbSet<StoreOrderItem> StoreOrderItems => Set<StoreOrderItem>();
public DbSet<StoreOrderItemDiscount> StoreOrderItemDiscounts => Set<StoreOrderItemDiscount>();
#endregion
#region ModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// store_category
modelBuilder.Entity<StoreCategory>(b =>
{
b.ToTable("store_category");
b.HasKey(x => x.Id);
b.Property(x => x.Title).HasMaxLength(256).IsRequired();
});
// store_item
modelBuilder.Entity<StoreItem>(b =>
{
b.ToTable("store_item");
b.HasKey(x => x.Id);
b.Property(x => x.ManaBuyPrice).IsRequired();
b.Property(x => x.ManaSellPrice).IsRequired();
b.Property(x => x.InventoryLimit).IsRequired();
b.Property(x => x.UnlimitedPurchase).HasDefaultValue(false);
b.HasOne(x => x.Category)
.WithMany(c => c.Items)
.HasForeignKey(x => x.StoreCategoryId)
.OnDelete(DeleteBehavior.Restrict);
});
// store_discount
modelBuilder.Entity<StoreDiscount>(b =>
{
b.ToTable("store_discount");
b.HasKey(x => x.Id);
b.Property(x => x.Percentage).HasPrecision(5, 2); // up to 100.00
b.Property(x => x.FromDate).IsRequired();
b.Property(x => x.UntilDate).IsRequired();
b.Property(x => x.IsCanceled).HasDefaultValue(false);
});
// store_discount_item (many-to-many manual mapping)
modelBuilder.Entity<StoreDiscountItem>(b =>
{
b.ToTable("store_discount_item");
b.HasKey(x => x.Id);
b.HasOne(x => x.Discount)
.WithMany(d => d.DiscountItems)
.HasForeignKey(x => x.StoreDiscountId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.StoreItem)
.WithMany(i => i.DiscountItems)
.HasForeignKey(x => x.StoreItemId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => new { x.StoreDiscountId, x.StoreItemId }).IsUnique();
});
// store_order
modelBuilder.Entity<StoreOrder>(b =>
{
b.ToTable("store_order");
b.HasKey(x => x.Id);
b.Property(x => x.UserId).IsRequired();
b.Property(x => x.CostUpdateDate).IsRequired();
b.Property(x => x.ItemsRedeemed).HasDefaultValue(false);
});
// store_order_item
modelBuilder.Entity<StoreOrderItem>(b =>
{
b.ToTable("store_order_item");
b.HasKey(x => x.Id);
b.Property(x => x.CalculatedPrice).IsRequired();
b.HasOne(x => x.Order)
.WithMany(o => o.OrderItems)
.HasForeignKey(x => x.StoreOrderId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.StoreItem)
.WithMany(i => i.OrderItems)
.HasForeignKey(x => x.StoreItemId)
.OnDelete(DeleteBehavior.Restrict);
});
// store_order_item_discount
modelBuilder.Entity<StoreOrderItemDiscount>(b =>
{
b.ToTable("store_order_item_discount");
b.HasKey(x => x.Id);
b.HasOne(x => x.OrderItem)
.WithMany(oi => oi.AppliedDiscounts)
.HasForeignKey(x => x.StoreOrderItemId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.Discount)
.WithMany(d => d.OrderItemDiscounts)
.HasForeignKey(x => x.StoreDiscountId)
.OnDelete(DeleteBehavior.Restrict);
b.HasIndex(x => new { x.StoreOrderItemId, x.StoreDiscountId }).IsUnique();
});
}
#endregion
}

View File

@@ -0,0 +1,17 @@
namespace StoreService.Database.Entities;
/// <summary>
/// Category grouping store items.
/// </summary>
public class StoreCategory
{
#region Properties
public long Id { get; set; }
public string Title { get; set; } = string.Empty;
#endregion
#region Navigation
public ICollection<StoreItem> Items { get; set; } = new List<StoreItem>();
#endregion
}

View File

@@ -0,0 +1,36 @@
using System.Diagnostics.CodeAnalysis;
namespace StoreService.Database.Entities;
/// <summary>
/// Percentage discount that can apply to one or more store items within a time window.
/// </summary>
public class StoreDiscount
{
#region Properties
public long Id { get; set; }
public decimal Percentage { get; set; } // 0 - 100 (%)
public string? Description { get; set; }
public DateTime FromDate { get; set; }
public DateTime UntilDate { get; set; }
public bool IsCanceled { get; set; }
#endregion
#region Navigation
public ICollection<StoreDiscountItem> DiscountItems { get; set; } = new List<StoreDiscountItem>();
public ICollection<StoreOrderItemDiscount> OrderItemDiscounts { get; set; } = new List<StoreOrderItemDiscount>();
#endregion
#region Helpers
/// <summary>
/// Checks whether discount is active at provided moment (default: now) ignoring cancellation flag (if canceled returns false).
/// </summary>
public bool IsActive(DateTime? at = null)
{
if (IsCanceled) return false;
var moment = at ?? DateTime.UtcNow;
return moment >= FromDate && moment <= UntilDate;
}
#endregion
}

View File

@@ -0,0 +1,19 @@
namespace StoreService.Database.Entities;
/// <summary>
/// Join entity linking discounts to store items.
/// </summary>
public class StoreDiscountItem
{
#region Properties
public long Id { get; set; }
public long StoreDiscountId { get; set; }
public long StoreItemId { get; set; }
#endregion
#region Navigation
public StoreDiscount? Discount { get; set; }
public StoreItem? StoreItem { get; set; }
#endregion
}

View File

@@ -0,0 +1,25 @@
namespace StoreService.Database.Entities;
/// <summary>
/// Store item with pricing and purchase rules.
/// </summary>
public class StoreItem
{
#region Properties
public long Id { get; set; }
public long ItemId { get; set; } // FK to external Item service (not modeled here)
public long StoreCategoryId { get; set; } // FK to StoreCategory
public long? RankId { get; set; } // Minimum rank required to buy (external system)
public int ManaBuyPrice { get; set; }
public int ManaSellPrice { get; set; }
public bool UnlimitedPurchase { get; set; }
public int InventoryLimit { get; set; }
#endregion
#region Navigation
public StoreCategory? Category { get; set; }
public ICollection<StoreDiscountItem> DiscountItems { get; set; } = new List<StoreDiscountItem>();
public ICollection<StoreOrderItem> OrderItems { get; set; } = new List<StoreOrderItem>();
#endregion
}

View File

@@ -0,0 +1,20 @@
namespace StoreService.Database.Entities;
/// <summary>
/// Represents a purchase order created by a user.
/// </summary>
public class StoreOrder
{
#region Properties
public long Id { get; set; }
public long UserId { get; set; }
public DateTime CostUpdateDate { get; set; } = DateTime.UtcNow; // updated when prices recalculated
public DateTime? PaidDate { get; set; } // when payment succeeded
public bool ItemsRedeemed { get; set; } // becomes true once items granted to inventory
#endregion
#region Navigation
public ICollection<StoreOrderItem> OrderItems { get; set; } = new List<StoreOrderItem>();
#endregion
}

View File

@@ -0,0 +1,21 @@
namespace StoreService.Database.Entities;
/// <summary>
/// Item line inside an order with captured calculated price for history.
/// </summary>
public class StoreOrderItem
{
#region Properties
public long Id { get; set; }
public long StoreOrderId { get; set; }
public long StoreItemId { get; set; }
public int CalculatedPrice { get; set; }
#endregion
#region Navigation
public StoreOrder? Order { get; set; }
public StoreItem? StoreItem { get; set; }
public ICollection<StoreOrderItemDiscount> AppliedDiscounts { get; set; } = new List<StoreOrderItemDiscount>();
#endregion
}

View File

@@ -0,0 +1,19 @@
namespace StoreService.Database.Entities;
/// <summary>
/// Captures which discounts were applied to order items at purchase time.
/// </summary>
public class StoreOrderItemDiscount
{
#region Properties
public long Id { get; set; }
public long StoreOrderItemId { get; set; }
public long? StoreDiscountId { get; set; } // can be null if discount later removed but kept for history
#endregion
#region Navigation
public StoreOrderItem? OrderItem { get; set; }
public StoreDiscount? Discount { get; set; }
#endregion
}