feat: add points persistence foundation
This commit is contained in:
74
Tiku.Domain/Commerce/PointEntities.cs
Normal file
74
Tiku.Domain/Commerce/PointEntities.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.Text.Json;
|
||||
using Tiku.Domain.Common;
|
||||
|
||||
namespace Tiku.Domain.Commerce;
|
||||
|
||||
public sealed class PointActivityTask : AuditableTenantEntity
|
||||
{
|
||||
public string TaskKey { get; set; } = string.Empty;
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public PointActivityTaskType TaskType { get; set; } = PointActivityTaskType.Manual;
|
||||
public PointActivityTaskStatus Status { get; set; } = PointActivityTaskStatus.Active;
|
||||
public int Points { get; set; }
|
||||
public int MaxClaimsPerUser { get; set; } = 1;
|
||||
public DateTimeOffset? StartsAt { get; set; }
|
||||
public DateTimeOffset? EndsAt { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public JsonElement Rules { get; set; } = JsonDefaults.Object();
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
}
|
||||
|
||||
public sealed class PointActivityClaim : AuditableTenantEntity
|
||||
{
|
||||
public Guid TaskId { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public string TaskKey { get; set; } = string.Empty;
|
||||
public int Points { get; set; }
|
||||
public PointActivityClaimStatus Status { get; set; } = PointActivityClaimStatus.Claimed;
|
||||
public string? SourceType { get; set; }
|
||||
public Guid? SourceId { get; set; }
|
||||
public DateTimeOffset ClaimedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
}
|
||||
|
||||
public sealed class PointExchangeItem : AuditableTenantEntity
|
||||
{
|
||||
public Guid? RegionId { get; set; }
|
||||
public string ItemKey { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public PointExchangeItemType ItemType { get; set; } = PointExchangeItemType.Entitlement;
|
||||
public PointExchangeItemStatus Status { get; set; } = PointExchangeItemStatus.Active;
|
||||
public int PointsCost { get; set; }
|
||||
public int? Stock { get; set; }
|
||||
public int? Days { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public DateTimeOffset? StartsAt { get; set; }
|
||||
public DateTimeOffset? EndsAt { get; set; }
|
||||
public JsonElement FulfillmentPayload { get; set; } = JsonDefaults.Object();
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
}
|
||||
|
||||
public sealed class PointExchangeOrder : AuditableTenantEntity
|
||||
{
|
||||
public Guid ItemId { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public string OrderNo { get; set; } = string.Empty;
|
||||
public string ItemName { get; set; } = string.Empty;
|
||||
public PointExchangeItemType ItemType { get; set; } = PointExchangeItemType.Entitlement;
|
||||
public PointExchangeOrderStatus Status { get; set; } = PointExchangeOrderStatus.Completed;
|
||||
public int PointsCost { get; set; }
|
||||
public DateTimeOffset OrderedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? CompletedAt { get; set; }
|
||||
public DateTimeOffset? CancelledAt { get; set; }
|
||||
public JsonElement FulfillmentSnapshot { get; set; } = JsonDefaults.Object();
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
}
|
||||
|
||||
public enum PointActivityTaskType { Manual, DailyLogin, Practice, VocabularyReview, BadgeUnlock }
|
||||
public enum PointActivityTaskStatus { Active, Disabled, Expired }
|
||||
public enum PointActivityClaimStatus { Claimed, Cancelled }
|
||||
public enum PointExchangeItemType { Entitlement, Coupon, Virtual, Other }
|
||||
public enum PointExchangeItemStatus { Active, Disabled, SoldOut }
|
||||
public enum PointExchangeOrderStatus { Pending, Completed, Cancelled, Failed }
|
||||
@@ -0,0 +1,112 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Tiku.Domain.Catalog;
|
||||
using Tiku.Domain.Commerce;
|
||||
using Tiku.Domain.Identity;
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Configurations;
|
||||
|
||||
internal sealed class PointActivityTaskConfiguration : IEntityTypeConfiguration<PointActivityTask>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PointActivityTask> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("point_activity_tasks");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.TaskKey).HasColumnType("citext").HasMaxLength(100);
|
||||
builder.Property(entity => entity.Title).HasMaxLength(200);
|
||||
builder.Property(entity => entity.Description).HasMaxLength(1000);
|
||||
builder.Property(entity => entity.TaskType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Rules).IsJson("{}");
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.TaskKey }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.SortOrder });
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_point_activity_tasks_points", "points > 0");
|
||||
table.HasCheckConstraint("ck_point_activity_tasks_max_claims", "max_claims_per_user > 0");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PointActivityClaimConfiguration : IEntityTypeConfiguration<PointActivityClaim>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PointActivityClaim> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("point_activity_claims");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.TaskKey).HasColumnType("citext").HasMaxLength(100);
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.SourceType).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.Property(entity => entity.ClaimedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.TaskId, entity.SourceType, entity.SourceId })
|
||||
.IsUnique()
|
||||
.HasFilter("source_type is not null and source_id is not null");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.CreatedAt });
|
||||
builder.HasOne<PointActivityTask>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.TaskId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_point_activity_claims_points", "points > 0");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PointExchangeItemConfiguration : IEntityTypeConfiguration<PointExchangeItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PointExchangeItem> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("point_exchange_items");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.ItemKey).HasColumnType("citext").HasMaxLength(100);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
builder.Property(entity => entity.Description).HasMaxLength(1000);
|
||||
builder.Property(entity => entity.ItemType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.FulfillmentPayload).IsJson("{}");
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.ItemKey }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.RegionId, entity.Status, entity.SortOrder });
|
||||
builder.HasOne<Region>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.RegionId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_point_exchange_items_points_cost", "points_cost > 0");
|
||||
table.HasCheckConstraint("ck_point_exchange_items_stock", "stock is null or stock >= 0");
|
||||
table.HasCheckConstraint("ck_point_exchange_items_days", "days is null or days >= 0");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PointExchangeOrderConfiguration : IEntityTypeConfiguration<PointExchangeOrder>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PointExchangeOrder> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("point_exchange_orders");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.OrderNo).HasMaxLength(100);
|
||||
builder.Property(entity => entity.ItemName).HasMaxLength(200);
|
||||
builder.Property(entity => entity.ItemType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.FulfillmentSnapshot).IsJson("{}");
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.Property(entity => entity.OrderedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.OrderNo }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.CreatedAt });
|
||||
builder.HasOne<PointExchangeItem>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.ItemId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_point_exchange_orders_points_cost", "points_cost > 0");
|
||||
});
|
||||
}
|
||||
}
|
||||
16165
Tiku.Infrastructure/Persistence/Migrations/20260726115242_AddPointsPersistenceFoundation.Designer.cs
generated
Normal file
16165
Tiku.Infrastructure/Persistence/Migrations/20260726115242_AddPointsPersistenceFoundation.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,265 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPointsPersistenceFoundation : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "point_activity_tasks",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
task_key = table.Column<string>(type: "citext", maxLength: 100, nullable: false),
|
||||
title = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
task_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
points = table.Column<int>(type: "integer", nullable: false),
|
||||
max_claims_per_user = table.Column<int>(type: "integer", nullable: false),
|
||||
starts_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
ends_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
sort_order = table.Column<int>(type: "integer", nullable: false),
|
||||
rules = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_point_activity_tasks", x => x.id);
|
||||
table.UniqueConstraint("ak_point_activity_tasks_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.CheckConstraint("ck_point_activity_tasks_max_claims", "max_claims_per_user > 0");
|
||||
table.CheckConstraint("ck_point_activity_tasks_points", "points > 0");
|
||||
table.ForeignKey(
|
||||
name: "fk_point_activity_tasks_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "point_exchange_items",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
region_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
item_key = table.Column<string>(type: "citext", maxLength: 100, nullable: false),
|
||||
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
item_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
points_cost = table.Column<int>(type: "integer", nullable: false),
|
||||
stock = table.Column<int>(type: "integer", nullable: true),
|
||||
days = table.Column<int>(type: "integer", nullable: true),
|
||||
sort_order = table.Column<int>(type: "integer", nullable: false),
|
||||
starts_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
ends_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
fulfillment_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_point_exchange_items", x => x.id);
|
||||
table.UniqueConstraint("ak_point_exchange_items_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.CheckConstraint("ck_point_exchange_items_days", "days is null or days >= 0");
|
||||
table.CheckConstraint("ck_point_exchange_items_points_cost", "points_cost > 0");
|
||||
table.CheckConstraint("ck_point_exchange_items_stock", "stock is null or stock >= 0");
|
||||
table.ForeignKey(
|
||||
name: "fk_point_exchange_items_regions_tenant_id_region_id",
|
||||
columns: x => new { x.tenant_id, x.region_id },
|
||||
principalTable: "regions",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "fk_point_exchange_items_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "point_activity_claims",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
task_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
task_key = table.Column<string>(type: "citext", maxLength: 100, nullable: false),
|
||||
points = table.Column<int>(type: "integer", nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
source_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
source_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
claimed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_point_activity_claims", x => x.id);
|
||||
table.UniqueConstraint("ak_point_activity_claims_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.CheckConstraint("ck_point_activity_claims_points", "points > 0");
|
||||
table.ForeignKey(
|
||||
name: "fk_point_activity_claims_point_activity_tasks_tenant_id_task_id",
|
||||
columns: x => new { x.tenant_id, x.task_id },
|
||||
principalTable: "point_activity_tasks",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "fk_point_activity_claims_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_point_activity_claims_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "point_exchange_orders",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
item_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
order_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
item_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
item_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
points_cost = table.Column<int>(type: "integer", nullable: false),
|
||||
ordered_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
completed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
cancelled_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
fulfillment_snapshot = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_point_exchange_orders", x => x.id);
|
||||
table.UniqueConstraint("ak_point_exchange_orders_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.CheckConstraint("ck_point_exchange_orders_points_cost", "points_cost > 0");
|
||||
table.ForeignKey(
|
||||
name: "fk_point_exchange_orders_point_exchange_items_tenant_id_item_id",
|
||||
columns: x => new { x.tenant_id, x.item_id },
|
||||
principalTable: "point_exchange_items",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "fk_point_exchange_orders_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_point_exchange_orders_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_activity_claims_tenant_id_task_id",
|
||||
table: "point_activity_claims",
|
||||
columns: new[] { "tenant_id", "task_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_activity_claims_tenant_id_user_id_created_at",
|
||||
table: "point_activity_claims",
|
||||
columns: new[] { "tenant_id", "user_id", "created_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_activity_claims_tenant_id_user_id_task_id_source_type~",
|
||||
table: "point_activity_claims",
|
||||
columns: new[] { "tenant_id", "user_id", "task_id", "source_type", "source_id" },
|
||||
unique: true,
|
||||
filter: "source_type is not null and source_id is not null");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_activity_claims_user_id",
|
||||
table: "point_activity_claims",
|
||||
column: "user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_activity_tasks_tenant_id_status_sort_order",
|
||||
table: "point_activity_tasks",
|
||||
columns: new[] { "tenant_id", "status", "sort_order" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_activity_tasks_tenant_id_task_key",
|
||||
table: "point_activity_tasks",
|
||||
columns: new[] { "tenant_id", "task_key" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_exchange_items_tenant_id_item_key",
|
||||
table: "point_exchange_items",
|
||||
columns: new[] { "tenant_id", "item_key" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_exchange_items_tenant_id_region_id_status_sort_order",
|
||||
table: "point_exchange_items",
|
||||
columns: new[] { "tenant_id", "region_id", "status", "sort_order" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_exchange_orders_tenant_id_item_id",
|
||||
table: "point_exchange_orders",
|
||||
columns: new[] { "tenant_id", "item_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_exchange_orders_tenant_id_order_no",
|
||||
table: "point_exchange_orders",
|
||||
columns: new[] { "tenant_id", "order_no" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_exchange_orders_tenant_id_user_id_created_at",
|
||||
table: "point_exchange_orders",
|
||||
columns: new[] { "tenant_id", "user_id", "created_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_point_exchange_orders_user_id",
|
||||
table: "point_exchange_orders",
|
||||
column: "user_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "point_activity_claims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "point_exchange_orders");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "point_activity_tasks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "point_exchange_items");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2614,6 +2614,434 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("payment_events", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityClaim", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<DateTimeOffset>("ClaimedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("claimed_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<JsonElement>("Metadata")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("metadata")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<int>("Points")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("points");
|
||||
|
||||
b.Property<Guid?>("SourceId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("source_id");
|
||||
|
||||
b.Property<string>("SourceType")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("source_type");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<Guid>("TaskId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("task_id");
|
||||
|
||||
b.Property<string>("TaskKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("citext")
|
||||
.HasColumnName("task_key");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_point_activity_claims");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_point_activity_claims_tenant_id_id");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_point_activity_claims_user_id");
|
||||
|
||||
b.HasIndex("TenantId", "TaskId")
|
||||
.HasDatabaseName("ix_point_activity_claims_tenant_id_task_id");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "CreatedAt")
|
||||
.HasDatabaseName("ix_point_activity_claims_tenant_id_user_id_created_at");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "TaskId", "SourceType", "SourceId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_point_activity_claims_tenant_id_user_id_task_id_source_type~")
|
||||
.HasFilter("source_type is not null and source_id is not null");
|
||||
|
||||
b.ToTable("point_activity_claims", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_point_activity_claims_points", "points > 0");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityTask", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<DateTimeOffset?>("EndsAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("ends_at");
|
||||
|
||||
b.Property<int>("MaxClaimsPerUser")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("max_claims_per_user");
|
||||
|
||||
b.Property<JsonElement>("Metadata")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("metadata")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<int>("Points")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("points");
|
||||
|
||||
b.Property<JsonElement>("Rules")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("rules")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("sort_order");
|
||||
|
||||
b.Property<DateTimeOffset?>("StartsAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("starts_at");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<string>("TaskKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("citext")
|
||||
.HasColumnName("task_key");
|
||||
|
||||
b.Property<string>("TaskType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("task_type");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("title");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_point_activity_tasks");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_point_activity_tasks_tenant_id_id");
|
||||
|
||||
b.HasIndex("TenantId", "TaskKey")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_point_activity_tasks_tenant_id_task_key");
|
||||
|
||||
b.HasIndex("TenantId", "Status", "SortOrder")
|
||||
.HasDatabaseName("ix_point_activity_tasks_tenant_id_status_sort_order");
|
||||
|
||||
b.ToTable("point_activity_tasks", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_point_activity_tasks_max_claims", "max_claims_per_user > 0");
|
||||
|
||||
t.HasCheckConstraint("ck_point_activity_tasks_points", "points > 0");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<int?>("Days")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("days");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("character varying(1000)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<DateTimeOffset?>("EndsAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("ends_at");
|
||||
|
||||
b.Property<JsonElement>("FulfillmentPayload")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("fulfillment_payload")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("ItemKey")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("citext")
|
||||
.HasColumnName("item_key");
|
||||
|
||||
b.Property<string>("ItemType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("item_type");
|
||||
|
||||
b.Property<JsonElement>("Metadata")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("metadata")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<int>("PointsCost")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("points_cost");
|
||||
|
||||
b.Property<Guid?>("RegionId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("region_id");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("sort_order");
|
||||
|
||||
b.Property<DateTimeOffset?>("StartsAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("starts_at");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<int?>("Stock")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("stock");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_point_exchange_items");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_point_exchange_items_tenant_id_id");
|
||||
|
||||
b.HasIndex("TenantId", "ItemKey")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_point_exchange_items_tenant_id_item_key");
|
||||
|
||||
b.HasIndex("TenantId", "RegionId", "Status", "SortOrder")
|
||||
.HasDatabaseName("ix_point_exchange_items_tenant_id_region_id_status_sort_order");
|
||||
|
||||
b.ToTable("point_exchange_items", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_point_exchange_items_days", "days is null or days >= 0");
|
||||
|
||||
t.HasCheckConstraint("ck_point_exchange_items_points_cost", "points_cost > 0");
|
||||
|
||||
t.HasCheckConstraint("ck_point_exchange_items_stock", "stock is null or stock >= 0");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeOrder", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<DateTimeOffset?>("CancelledAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("cancelled_at");
|
||||
|
||||
b.Property<DateTimeOffset?>("CompletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("completed_at");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<JsonElement>("FulfillmentSnapshot")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("fulfillment_snapshot")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<Guid>("ItemId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("item_id");
|
||||
|
||||
b.Property<string>("ItemName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("item_name");
|
||||
|
||||
b.Property<string>("ItemType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("item_type");
|
||||
|
||||
b.Property<JsonElement>("Metadata")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("metadata")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("OrderNo")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("order_no");
|
||||
|
||||
b.Property<DateTimeOffset>("OrderedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("ordered_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<int>("PointsCost")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("points_cost");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("user_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_point_exchange_orders");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_point_exchange_orders_tenant_id_id");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("ix_point_exchange_orders_user_id");
|
||||
|
||||
b.HasIndex("TenantId", "ItemId")
|
||||
.HasDatabaseName("ix_point_exchange_orders_tenant_id_item_id");
|
||||
|
||||
b.HasIndex("TenantId", "OrderNo")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_point_exchange_orders_tenant_id_order_no");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "CreatedAt")
|
||||
.HasDatabaseName("ix_point_exchange_orders_tenant_id_user_id_created_at");
|
||||
|
||||
b.ToTable("point_exchange_orders", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("ck_point_exchange_orders_points_cost", "points_cost > 0");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -13315,6 +13743,83 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasConstraintName("fk_payment_events_payments_tenant_id_payment_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityClaim", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_point_activity_claims_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_point_activity_claims_users_user_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Commerce.PointActivityTask", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "TaskId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_point_activity_claims_point_activity_tasks_tenant_id_task_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.PointActivityTask", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_point_activity_tasks_tenants_tenant_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeItem", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_point_exchange_items_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Catalog.Region", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "RegionId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("fk_point_exchange_items_regions_tenant_id_region_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.PointExchangeOrder", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_point_exchange_orders_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Identity.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_point_exchange_orders_users_user_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Commerce.PointExchangeItem", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "ItemId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_point_exchange_orders_point_exchange_items_tenant_id_item_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Commerce.Product", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
|
||||
@@ -109,6 +109,10 @@ public sealed class TikuDbContext(DbContextOptions<TikuDbContext> options) : DbC
|
||||
public DbSet<CommerceReconciliationItem> CommerceReconciliationItems => Set<CommerceReconciliationItem>();
|
||||
public DbSet<CommerceReconciliationIssue> CommerceReconciliationIssues => Set<CommerceReconciliationIssue>();
|
||||
public DbSet<CommerceReconciliationIssueEvent> CommerceReconciliationIssueEvents => Set<CommerceReconciliationIssueEvent>();
|
||||
public DbSet<PointActivityTask> PointActivityTasks => Set<PointActivityTask>();
|
||||
public DbSet<PointActivityClaim> PointActivityClaims => Set<PointActivityClaim>();
|
||||
public DbSet<PointExchangeItem> PointExchangeItems => Set<PointExchangeItem>();
|
||||
public DbSet<PointExchangeOrder> PointExchangeOrders => Set<PointExchangeOrder>();
|
||||
public DbSet<ReferralTrack> ReferralTracks => Set<ReferralTrack>();
|
||||
public DbSet<ReferralCode> ReferralCodes => Set<ReferralCode>();
|
||||
public DbSet<ReferralLead> ReferralLeads => Set<ReferralLead>();
|
||||
|
||||
@@ -31,7 +31,7 @@ public sealed class PersistenceModelTests
|
||||
.Select(entity => entity.GetTableName())
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
Assert.Equal(127, tableNames.Count);
|
||||
Assert.Equal(131, tableNames.Count);
|
||||
Assert.Contains("tenants", tableNames);
|
||||
Assert.Contains("tenant_settings", tableNames);
|
||||
Assert.Contains("content_entries", tableNames);
|
||||
@@ -96,6 +96,10 @@ public sealed class PersistenceModelTests
|
||||
Assert.Contains("commerce_reconciliation_items", tableNames);
|
||||
Assert.Contains("commerce_reconciliation_issues", tableNames);
|
||||
Assert.Contains("commerce_reconciliation_issue_events", tableNames);
|
||||
Assert.Contains("point_activity_tasks", tableNames);
|
||||
Assert.Contains("point_activity_claims", tableNames);
|
||||
Assert.Contains("point_exchange_items", tableNames);
|
||||
Assert.Contains("point_exchange_orders", tableNames);
|
||||
Assert.Contains("content_asset_access_events", tableNames);
|
||||
Assert.Contains("content_asset_security_scan_events", tableNames);
|
||||
Assert.Contains("question_bank_grants", tableNames);
|
||||
@@ -661,6 +665,13 @@ public sealed class PersistenceModelTests
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(PointActivityTask), nameof(PointActivityTask.Rules), "'{}'::jsonb")]
|
||||
[InlineData(typeof(PointActivityTask), nameof(PointActivityTask.Metadata), "'{}'::jsonb")]
|
||||
[InlineData(typeof(PointActivityClaim), nameof(PointActivityClaim.Metadata), "'{}'::jsonb")]
|
||||
[InlineData(typeof(PointExchangeItem), nameof(PointExchangeItem.FulfillmentPayload), "'{}'::jsonb")]
|
||||
[InlineData(typeof(PointExchangeItem), nameof(PointExchangeItem.Metadata), "'{}'::jsonb")]
|
||||
[InlineData(typeof(PointExchangeOrder), nameof(PointExchangeOrder.FulfillmentSnapshot), "'{}'::jsonb")]
|
||||
[InlineData(typeof(PointExchangeOrder), nameof(PointExchangeOrder.Metadata), "'{}'::jsonb")]
|
||||
[InlineData(typeof(ReferralTrack), nameof(ReferralTrack.Metadata), "'{}'::jsonb")]
|
||||
[InlineData(typeof(ReferralCode), nameof(ReferralCode.Metadata), "'{}'::jsonb")]
|
||||
[InlineData(typeof(ReferralLead), nameof(ReferralLead.Metadata), "'{}'::jsonb")]
|
||||
@@ -693,6 +704,9 @@ public sealed class PersistenceModelTests
|
||||
{
|
||||
using var context = new TikuDbContext(Options);
|
||||
|
||||
AssertHasUniqueIndex<PointActivityTask>(nameof(PointActivityTask.TenantId), nameof(PointActivityTask.TaskKey));
|
||||
AssertHasUniqueIndex<PointExchangeItem>(nameof(PointExchangeItem.TenantId), nameof(PointExchangeItem.ItemKey));
|
||||
AssertHasUniqueIndex<PointExchangeOrder>(nameof(PointExchangeOrder.TenantId), nameof(PointExchangeOrder.OrderNo));
|
||||
AssertHasUniqueIndex<ReferralCode>(nameof(ReferralCode.TenantId), nameof(ReferralCode.Code));
|
||||
AssertHasUniqueIndex<ReferralLead>(nameof(ReferralLead.TenantId), nameof(ReferralLead.StudentUserId));
|
||||
AssertHasUniqueIndex<CrmConfig>(nameof(CrmConfig.TenantId));
|
||||
|
||||
Reference in New Issue
Block a user