Implemented order ui and validation. Added coupon handling
This commit is contained in:
8
Moonlight/App/Database/Entities/Store/Transaction.cs
Normal file
8
Moonlight/App/Database/Entities/Store/Transaction.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Moonlight.App.Database.Entities.Store;
|
||||||
|
|
||||||
|
public class Transaction
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public double Price { get; set; }
|
||||||
|
public string Text { get; set; } = "";
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities.Store;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Database.Entities;
|
||||||
|
|
||||||
public class User
|
public class User
|
||||||
{
|
{
|
||||||
@@ -10,6 +12,11 @@ public class User
|
|||||||
public string? TotpKey { get; set; } = null;
|
public string? TotpKey { get; set; } = null;
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
|
public double Balance { get; set; }
|
||||||
|
public List<Transaction> Transactions { get; set; } = new();
|
||||||
|
|
||||||
|
public List<CouponUse> CouponUses { get; set; } = new();
|
||||||
|
public List<GiftCodeUse> GiftCodeUses { get; set; } = new();
|
||||||
|
|
||||||
// Meta data
|
// Meta data
|
||||||
public string Flags { get; set; } = "";
|
public string Flags { get; set; } = "";
|
||||||
|
|||||||
371
Moonlight/App/Database/Migrations/20231018203522_AddedUserStoreData.Designer.cs
generated
Normal file
371
Moonlight/App/Database/Migrations/20231018203522_AddedUserStoreData.Designer.cs
generated
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Moonlight.App.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.App.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DataContext))]
|
||||||
|
[Migration("20231018203522_AddedUserStoreData")]
|
||||||
|
partial class AddedUserStoreData
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Category", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Categories");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Coupon", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Amount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Code")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Percent")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Coupons");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("CouponId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CouponId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("CouponUses");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Amount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Code")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Value")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("GiftCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("GiftCodeId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GiftCodeId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("GiftCodeUses");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ConfigJson")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Duration")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MaxPerUser")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Stock")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryId");
|
||||||
|
|
||||||
|
b.ToTable("Products");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ConfigJsonOverride")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Nickname")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("OwnerId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ProductId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RenewAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Suspended")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("OwnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ProductId");
|
||||||
|
|
||||||
|
b.ToTable("Services");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("ServiceId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServiceId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("ServiceShares");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Avatar")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Balance")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Flags")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Permissions")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("TokenValidTimestamp")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TotpKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.Coupon", "Coupon")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CouponId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", null)
|
||||||
|
.WithMany("CouponUses")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("Coupon");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.GiftCode", "GiftCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GiftCodeId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", null)
|
||||||
|
.WithMany("GiftCodeUses")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("GiftCode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Category");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("OwnerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.Product", "Product")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProductId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Owner");
|
||||||
|
|
||||||
|
b.Navigation("Product");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.Service", null)
|
||||||
|
.WithMany("Shares")
|
||||||
|
.HasForeignKey("ServiceId");
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Shares");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("CouponUses");
|
||||||
|
|
||||||
|
b.Navigation("GiftCodeUses");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.App.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedUserStoreData : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Products_Categories_CategoryId",
|
||||||
|
table: "Products");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "Balance",
|
||||||
|
table: "Users",
|
||||||
|
type: "REAL",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0.0);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<int>(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Products",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
oldClrType: typeof(int),
|
||||||
|
oldType: "INTEGER",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "UserId",
|
||||||
|
table: "GiftCodeUses",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "UserId",
|
||||||
|
table: "CouponUses",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GiftCodeUses_UserId",
|
||||||
|
table: "GiftCodeUses",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_CouponUses_UserId",
|
||||||
|
table: "CouponUses",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_CouponUses_Users_UserId",
|
||||||
|
table: "CouponUses",
|
||||||
|
column: "UserId",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_GiftCodeUses_Users_UserId",
|
||||||
|
table: "GiftCodeUses",
|
||||||
|
column: "UserId",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Products_Categories_CategoryId",
|
||||||
|
table: "Products",
|
||||||
|
column: "CategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_CouponUses_Users_UserId",
|
||||||
|
table: "CouponUses");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_GiftCodeUses_Users_UserId",
|
||||||
|
table: "GiftCodeUses");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Products_Categories_CategoryId",
|
||||||
|
table: "Products");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_GiftCodeUses_UserId",
|
||||||
|
table: "GiftCodeUses");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_CouponUses_UserId",
|
||||||
|
table: "CouponUses");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Balance",
|
||||||
|
table: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserId",
|
||||||
|
table: "GiftCodeUses");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserId",
|
||||||
|
table: "CouponUses");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<int>(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "Products",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(int),
|
||||||
|
oldType: "INTEGER");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Products_Categories_CategoryId",
|
||||||
|
table: "Products",
|
||||||
|
column: "CategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
403
Moonlight/App/Database/Migrations/20231018204737_AddedTransactions.Designer.cs
generated
Normal file
403
Moonlight/App/Database/Migrations/20231018204737_AddedTransactions.Designer.cs
generated
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Moonlight.App.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.App.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DataContext))]
|
||||||
|
[Migration("20231018204737_AddedTransactions")]
|
||||||
|
partial class AddedTransactions
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Category", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Categories");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Coupon", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Amount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Code")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Percent")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Coupons");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("CouponId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CouponId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("CouponUses");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Amount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Code")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Value")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("GiftCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("GiftCodeId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GiftCodeId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("GiftCodeUses");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ConfigJson")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Duration")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MaxPerUser")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Stock")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryId");
|
||||||
|
|
||||||
|
b.ToTable("Products");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ConfigJsonOverride")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Nickname")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("OwnerId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ProductId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RenewAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Suspended")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("OwnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ProductId");
|
||||||
|
|
||||||
|
b.ToTable("Services");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("ServiceId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServiceId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("ServiceShares");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<string>("Text")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Transaction");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Avatar")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Balance")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Flags")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Permissions")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("TokenValidTimestamp")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TotpKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.Coupon", "Coupon")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CouponId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", null)
|
||||||
|
.WithMany("CouponUses")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("Coupon");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.GiftCode", "GiftCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GiftCodeId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", null)
|
||||||
|
.WithMany("GiftCodeUses")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("GiftCode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Category");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("OwnerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.Product", "Product")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProductId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Owner");
|
||||||
|
|
||||||
|
b.Navigation("Product");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.Store.Service", null)
|
||||||
|
.WithMany("Shares")
|
||||||
|
.HasForeignKey("ServiceId");
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", null)
|
||||||
|
.WithMany("Transactions")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Shares");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("CouponUses");
|
||||||
|
|
||||||
|
b.Navigation("GiftCodeUses");
|
||||||
|
|
||||||
|
b.Navigation("Transactions");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.App.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedTransactions : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Transaction",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Price = table.Column<double>(type: "REAL", nullable: false),
|
||||||
|
Text = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
UserId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Transaction", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Transaction_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Transaction_UserId",
|
||||||
|
table: "Transaction",
|
||||||
|
column: "UserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Transaction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,10 +70,15 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Property<int>("CouponId")
|
b.Property<int>("CouponId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("CouponId");
|
b.HasIndex("CouponId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("CouponUses");
|
b.ToTable("CouponUses");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,10 +112,15 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Property<int>("GiftCodeId")
|
b.Property<int>("GiftCodeId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("GiftCodeId");
|
b.HasIndex("GiftCodeId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("GiftCodeUses");
|
b.ToTable("GiftCodeUses");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -120,7 +130,7 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int?>("CategoryId")
|
b.Property<int>("CategoryId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("ConfigJson")
|
b.Property<string>("ConfigJson")
|
||||||
@@ -221,6 +231,29 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("ServiceShares");
|
b.ToTable("ServiceShares");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<string>("Text")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Transaction");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -230,6 +263,9 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Property<string>("Avatar")
|
b.Property<string>("Avatar")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Balance")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
b.Property<DateTime>("CreatedAt")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
@@ -271,6 +307,10 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", null)
|
||||||
|
.WithMany("CouponUses")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
b.Navigation("Coupon");
|
b.Navigation("Coupon");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -282,6 +322,10 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", null)
|
||||||
|
.WithMany("GiftCodeUses")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
b.Navigation("GiftCode");
|
b.Navigation("GiftCode");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -289,7 +333,9 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
{
|
{
|
||||||
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
|
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("CategoryId");
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("Category");
|
b.Navigation("Category");
|
||||||
});
|
});
|
||||||
@@ -328,10 +374,26 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.User", null)
|
||||||
|
.WithMany("Transactions")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Shares");
|
b.Navigation("Shares");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("CouponUses");
|
||||||
|
|
||||||
|
b.Navigation("GiftCodeUses");
|
||||||
|
|
||||||
|
b.Navigation("Transactions");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
87
Moonlight/App/Services/Store/StoreOrderService.cs
Normal file
87
Moonlight/App/Services/Store/StoreOrderService.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Database.Entities.Store;
|
||||||
|
using Moonlight.App.Exceptions;
|
||||||
|
using Moonlight.App.Repositories;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services.Store;
|
||||||
|
|
||||||
|
public class StoreOrderService
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||||
|
|
||||||
|
public StoreOrderService(IServiceScopeFactory serviceScopeFactory)
|
||||||
|
{
|
||||||
|
ServiceScopeFactory = serviceScopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Validate(User u, Product p, int durationMultiplier, Coupon? c)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var productRepo = scope.ServiceProvider.GetRequiredService<Repository<Product>>();
|
||||||
|
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
||||||
|
var serviceRepo = scope.ServiceProvider.GetRequiredService<Repository<Service>>();
|
||||||
|
var couponRepo = scope.ServiceProvider.GetRequiredService<Repository<Coupon>>();
|
||||||
|
|
||||||
|
// Ensure the values are safe and loaded by using the created scope to bypass the cache
|
||||||
|
|
||||||
|
var user = userRepo
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.CouponUses)
|
||||||
|
.ThenInclude(x => x.Coupon)
|
||||||
|
.FirstOrDefault(x => x.Id == u.Id);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
throw new DisplayException("Unsafe value detected. Please reload the page to proceed");
|
||||||
|
|
||||||
|
|
||||||
|
var product = productRepo
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.Id == p.Id);
|
||||||
|
|
||||||
|
if (product == null)
|
||||||
|
throw new DisplayException("Unsafe value detected. Please reload the page to proceed");
|
||||||
|
|
||||||
|
|
||||||
|
Coupon? coupon = c;
|
||||||
|
|
||||||
|
if (coupon != null) // Only check if the coupon actually has a value
|
||||||
|
{
|
||||||
|
coupon = couponRepo
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.Id == coupon.Id);
|
||||||
|
|
||||||
|
if(coupon == null)
|
||||||
|
throw new DisplayException("Unsafe value detected. Please reload the page to proceed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform checks on selected order
|
||||||
|
|
||||||
|
if (coupon != null && user.CouponUses.Any(x => x.Coupon.Id == coupon.Id))
|
||||||
|
throw new DisplayException("Coupon already used");
|
||||||
|
|
||||||
|
if (coupon != null && coupon.Amount == 0)
|
||||||
|
throw new DisplayException("No coupon uses left");
|
||||||
|
|
||||||
|
var price = product.Price * durationMultiplier;
|
||||||
|
|
||||||
|
if (coupon != null)
|
||||||
|
price = Math.Round(price * coupon.Percent / 100, 2);
|
||||||
|
|
||||||
|
if (user.Balance < price)
|
||||||
|
throw new DisplayException("Order is too expensive");
|
||||||
|
|
||||||
|
var userServices = serviceRepo
|
||||||
|
.Get()
|
||||||
|
.Where(x => x.Product.Id == product.Id)
|
||||||
|
.Count(x => x.Owner.Id == user.Id);
|
||||||
|
|
||||||
|
if (userServices >= product.MaxPerUser)
|
||||||
|
throw new DisplayException("The limit of this product on your account has been reached");
|
||||||
|
|
||||||
|
if (product.Stock < 1)
|
||||||
|
throw new DisplayException("The product is out of stock");
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ public class StoreService
|
|||||||
private readonly IServiceProvider ServiceProvider;
|
private readonly IServiceProvider ServiceProvider;
|
||||||
|
|
||||||
public StoreAdminService Admin => ServiceProvider.GetRequiredService<StoreAdminService>();
|
public StoreAdminService Admin => ServiceProvider.GetRequiredService<StoreAdminService>();
|
||||||
|
public StoreOrderService Order => ServiceProvider.GetRequiredService<StoreOrderService>();
|
||||||
|
|
||||||
public StoreService(IServiceProvider serviceProvider)
|
public StoreService(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ builder.Services.AddScoped<AlertService>();
|
|||||||
// Services / Store
|
// Services / Store
|
||||||
builder.Services.AddScoped<StoreService>();
|
builder.Services.AddScoped<StoreService>();
|
||||||
builder.Services.AddScoped<StoreAdminService>();
|
builder.Services.AddScoped<StoreAdminService>();
|
||||||
|
builder.Services.AddScoped<StoreOrderService>();
|
||||||
|
|
||||||
// Services / Users
|
// Services / Users
|
||||||
builder.Services.AddScoped<UserService>();
|
builder.Services.AddScoped<UserService>();
|
||||||
|
|||||||
230
Moonlight/Shared/Views/Store/Order.razor
Normal file
230
Moonlight/Shared/Views/Store/Order.razor
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
@page "/store/order/{slug}"
|
||||||
|
|
||||||
|
@using Moonlight.App.Services.Store
|
||||||
|
@using Moonlight.App.Services
|
||||||
|
@using Moonlight.App.Database.Entities.Store
|
||||||
|
@using Moonlight.App.Repositories
|
||||||
|
|
||||||
|
@inject ConfigService ConfigService
|
||||||
|
@inject StoreService StoreService
|
||||||
|
@inject IdentityService IdentityService
|
||||||
|
@inject AlertService AlertService
|
||||||
|
@inject Repository<Product> ProductRepository
|
||||||
|
@inject Repository<Coupon> CouponRepository
|
||||||
|
|
||||||
|
<LazyLoader Load="Load">
|
||||||
|
@if (SelectedProduct == null)
|
||||||
|
{
|
||||||
|
@*
|
||||||
|
TODO: Add 404 here
|
||||||
|
*@
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-7 col-12">
|
||||||
|
<div class="card mb-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="fs-2x fw-bold mb-10">@(SelectedProduct.Name)</h2>
|
||||||
|
<p class="text-gray-400 fs-4 fw-semibold">
|
||||||
|
@Formatter.FormatLineBreaks(SelectedProduct.Description)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<div class="col-md-4 col-12">
|
||||||
|
<a @onclick="() => SetDurationMultiplicator(1)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplicator == 1 ? "border border-info" : "")">
|
||||||
|
<h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 1) days</h4>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 col-12">
|
||||||
|
<a @onclick="() => SetDurationMultiplicator(2)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplicator == 2 ? "border border-info" : "")">
|
||||||
|
<h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 2) days</h4>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 col-12">
|
||||||
|
<a @onclick="() => SetDurationMultiplicator(3)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplicator == 3 ? "border border-info" : "")">
|
||||||
|
<h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 3) days</h4>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card mb-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="fs-1x fw-bold mb-10">Apply coupon codes</h3>
|
||||||
|
<div class="input-group">
|
||||||
|
<input @bind="CouponCode" class="form-control form-control-solid" placeholder="Coupon code..."/>
|
||||||
|
<button @onclick="ApplyCoupon" class="btn btn-primary">Apply</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1 col-12"></div>
|
||||||
|
<div class="col-md-3 col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Summary</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@{
|
||||||
|
var defaultPrice = SelectedProduct.Price * DurationMultiplicator;
|
||||||
|
double actualPrice;
|
||||||
|
|
||||||
|
if (SelectedCoupon == null)
|
||||||
|
actualPrice = defaultPrice;
|
||||||
|
else
|
||||||
|
actualPrice = Math.Round(defaultPrice * SelectedCoupon.Percent / 100, 2);
|
||||||
|
|
||||||
|
var currency = ConfigService.Get().Store.Currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="card-body fs-5">
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Today</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(currency) @(defaultPrice)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Renew</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(currency) @(defaultPrice)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Duration</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(SelectedProduct.Duration * DurationMultiplicator) days</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="separator my-4"></div>
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Discount</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(SelectedCoupon?.Percent ?? 0)%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Coupon</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(SelectedCoupon?.Code ?? "None")</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="separator my-4"></div>
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Total</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(currency) @(actualPrice)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-7">
|
||||||
|
@if (!CanBeOrdered && !string.IsNullOrEmpty(ErrorMessage))
|
||||||
|
{
|
||||||
|
<div class="alert alert-warning bg-warning text-white text-center">
|
||||||
|
@ErrorMessage
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
@if (IsValidating)
|
||||||
|
{
|
||||||
|
<button class="btn btn-primary w-100 disabled" disabled="">
|
||||||
|
<span class="spinner-border spinner-border-sm align-middle"></span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (CanBeOrdered)
|
||||||
|
{
|
||||||
|
<button class="btn btn-primary w-100">Order for @(currency) @(actualPrice)</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<button class="btn btn-primary w-100 disabled" disabled="">Order for @(currency) @(actualPrice)</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1 col-12"></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</LazyLoader>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
private Product? SelectedProduct;
|
||||||
|
private Coupon? SelectedCoupon;
|
||||||
|
private int DurationMultiplicator = 1;
|
||||||
|
|
||||||
|
private string CouponCode = "";
|
||||||
|
|
||||||
|
private bool CanBeOrdered = false;
|
||||||
|
private bool IsValidating = false;
|
||||||
|
private string ErrorMessage = "";
|
||||||
|
|
||||||
|
private async Task SetDurationMultiplicator(int i)
|
||||||
|
{
|
||||||
|
DurationMultiplicator = i;
|
||||||
|
|
||||||
|
await Revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ApplyCoupon()
|
||||||
|
{
|
||||||
|
SelectedCoupon = CouponRepository
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.Code == CouponCode);
|
||||||
|
|
||||||
|
CouponCode = "";
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
if (SelectedCoupon == null)
|
||||||
|
{
|
||||||
|
await AlertService.Error("", "Invalid coupon code entered");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task Revalidate()
|
||||||
|
{
|
||||||
|
if (SelectedProduct == null) // Prevent validating null
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
IsValidating = true;
|
||||||
|
InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await StoreService.Order.Validate(IdentityService.CurrentUser, SelectedProduct, 1, SelectedCoupon);
|
||||||
|
CanBeOrdered = true;
|
||||||
|
}
|
||||||
|
catch (DisplayException e)
|
||||||
|
{
|
||||||
|
CanBeOrdered = false;
|
||||||
|
ErrorMessage = e.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsValidating = false;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Load(LazyLoader _)
|
||||||
|
{
|
||||||
|
SelectedProduct = ProductRepository
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.Slug == Slug);
|
||||||
|
|
||||||
|
await Revalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user