chore: squash migrations into initial schema

This commit is contained in:
xiong
2026-07-26 06:09:10 +08:00
parent 2451c4b7a8
commit 40025f5b4a
27 changed files with 8533 additions and 114611 deletions

View File

@@ -1,932 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddContentNavigationAndTenantConfiguration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
.Annotation("Npgsql:PostgresExtension:citext", ",,")
.Annotation("Npgsql:PostgresExtension:ltree", ",,")
.OldAnnotation("Npgsql:PostgresExtension:citext", ",,");
migrationBuilder.AddColumn<Guid>(
name: "content_node_id",
table: "questions",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "entry_id",
table: "questions",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<JsonElement>(
name: "exam_markers",
table: "questions",
type: "jsonb",
nullable: false,
defaultValueSql: "'{}'::jsonb");
migrationBuilder.AddColumn<Guid>(
name: "primary_collection_id",
table: "questions",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "blueprint_id",
table: "practice_sessions",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "collection_id",
table: "practice_sessions",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "content_node_id",
table: "practice_sessions",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "duration_minutes",
table: "practice_sessions",
type: "integer",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "entry_id",
table: "practice_sessions",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<DateTimeOffset>(
name: "expires_at",
table: "practice_sessions",
type: "timestamp with time zone",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "question_count",
table: "practice_sessions",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<JsonElement>(
name: "question_ids",
table: "practice_sessions",
type: "jsonb",
nullable: false,
defaultValueSql: "'[]'::jsonb");
migrationBuilder.AddColumn<decimal>(
name: "total_score",
table: "practice_sessions",
type: "numeric(8,2)",
precision: 8,
scale: 2,
nullable: true);
migrationBuilder.CreateTable(
name: "content_entries",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
entry_key = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
entry_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
icon = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
route = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
description = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
visibility = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
access_rules = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
layout_config = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_content_entries", x => x.id);
table.UniqueConstraint("ak_content_entries_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_content_entries_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_content_entries_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_entries_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_content_entries_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "tenant_branding",
columns: table => new
{
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
brand_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
short_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
slogan = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
organization_name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
logo_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
favicon_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
service_wechat = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
service_account_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
theme = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
public_assets = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
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_tenant_branding", x => x.tenant_id);
table.ForeignKey(
name: "fk_tenant_branding_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_domains",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
host = table.Column<string>(type: "citext", maxLength: 253, nullable: false),
domain_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
is_primary = table.Column<bool>(type: "boolean", nullable: false),
verification_token = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
verified_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_tenant_domains", x => x.id);
table.UniqueConstraint("ak_tenant_domains_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_domains_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_settings",
columns: table => new
{
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
feature_flags = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
admin_feature_flags = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
public_config = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
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_tenant_settings", x => x.tenant_id);
table.ForeignKey(
name: "fk_tenant_settings_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "content_nodes",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
entry_id = table.Column<Guid>(type: "uuid", nullable: false),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
parent_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
node_key = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
node_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
marker_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
marker_config = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
path = table.Column<string>(type: "ltree", nullable: true),
depth = table.Column<int>(type: "integer", nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
is_selectable = table.Column<bool>(type: "boolean", nullable: false),
is_leaf = table.Column<bool>(type: "boolean", nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_content_nodes", x => x.id);
table.UniqueConstraint("ak_content_nodes_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_content_nodes_depth", "depth >= 0");
table.ForeignKey(
name: "fk_content_nodes_content_entries_tenant_id_entry_id",
columns: x => new { x.tenant_id, x.entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_nodes_content_nodes_tenant_id_parent_id",
columns: x => new { x.tenant_id, x.parent_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_nodes_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_content_nodes_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_nodes_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_content_nodes_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "question_collections",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
entry_id = table.Column<Guid>(type: "uuid", nullable: true),
node_id = table.Column<Guid>(type: "uuid", nullable: true),
subject_id = table.Column<Guid>(type: "uuid", nullable: true),
category_id = table.Column<Guid>(type: "uuid", nullable: true),
question_bank_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
collection_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
source_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
filters = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
question_count = table.Column<int>(type: "integer", nullable: false),
total_score = table.Column<decimal>(type: "numeric(8,2)", precision: 8, scale: 2, nullable: true),
duration_minutes = table.Column<int>(type: "integer", nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_question_collections", x => x.id);
table.UniqueConstraint("ak_question_collections_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_question_collections_duration", "duration_minutes is null or duration_minutes > 0");
table.CheckConstraint("ck_question_collections_question_count", "question_count >= 0");
table.ForeignKey(
name: "fk_question_collections_categories_tenant_id_category_id",
columns: x => new { x.tenant_id, x.category_id },
principalTable: "categories",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_question_collections_content_entries_tenant_id_entry_id",
columns: x => new { x.tenant_id, x.entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_question_collections_content_nodes_tenant_id_node_id",
columns: x => new { x.tenant_id, x.node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_question_collections_question_banks_tenant_id_question_bank~",
columns: x => new { x.tenant_id, x.question_bank_id },
principalTable: "question_banks",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_question_collections_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_question_collections_subjects_tenant_id_subject_id",
columns: x => new { x.tenant_id, x.subject_id },
principalTable: "subjects",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_question_collections_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_question_collections_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_question_collections_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "practice_blueprints",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
entry_id = table.Column<Guid>(type: "uuid", nullable: true),
node_id = table.Column<Guid>(type: "uuid", nullable: true),
collection_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
assembly_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
question_limit = table.Column<int>(type: "integer", nullable: true),
duration_minutes = table.Column<int>(type: "integer", nullable: true),
total_score = table.Column<decimal>(type: "numeric(8,2)", precision: 8, scale: 2, nullable: true),
pass_score = table.Column<decimal>(type: "numeric(8,2)", precision: 8, scale: 2, nullable: true),
sections = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
rules = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_practice_blueprints", x => x.id);
table.UniqueConstraint("ak_practice_blueprints_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_practice_blueprints_duration", "duration_minutes is null or duration_minutes > 0");
table.CheckConstraint("ck_practice_blueprints_question_limit", "question_limit is null or question_limit > 0");
table.ForeignKey(
name: "fk_practice_blueprints_content_entries_tenant_id_entry_id",
columns: x => new { x.tenant_id, x.entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_practice_blueprints_content_nodes_tenant_id_node_id",
columns: x => new { x.tenant_id, x.node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_practice_blueprints_question_collections_tenant_id_collecti~",
columns: x => new { x.tenant_id, x.collection_id },
principalTable: "question_collections",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_practice_blueprints_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_practice_blueprints_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_practice_blueprints_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_practice_blueprints_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "question_collection_items",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
collection_id = table.Column<Guid>(type: "uuid", nullable: false),
question_id = table.Column<Guid>(type: "uuid", nullable: false),
section_key = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
sort_order = table.Column<int>(type: "integer", nullable: false),
score = table.Column<decimal>(type: "numeric(8,2)", precision: 8, scale: 2, nullable: true),
required = table.Column<bool>(type: "boolean", nullable: false),
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_question_collection_items", x => x.id);
table.UniqueConstraint("ak_question_collection_items_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_question_collection_items_question_collections_tenant_id_co~",
columns: x => new { x.tenant_id, x.collection_id },
principalTable: "question_collections",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_question_collection_items_questions_tenant_id_question_id",
columns: x => new { x.tenant_id, x.question_id },
principalTable: "questions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_question_collection_items_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_questions_tenant_id_content_node_id",
table: "questions",
columns: new[] { "tenant_id", "content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_questions_tenant_id_entry_id",
table: "questions",
columns: new[] { "tenant_id", "entry_id" });
migrationBuilder.CreateIndex(
name: "ix_questions_tenant_id_primary_collection_id",
table: "questions",
columns: new[] { "tenant_id", "primary_collection_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_sessions_tenant_id_blueprint_id",
table: "practice_sessions",
columns: new[] { "tenant_id", "blueprint_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_sessions_tenant_id_collection_id",
table: "practice_sessions",
columns: new[] { "tenant_id", "collection_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_sessions_tenant_id_content_node_id",
table: "practice_sessions",
columns: new[] { "tenant_id", "content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_sessions_tenant_id_entry_id",
table: "practice_sessions",
columns: new[] { "tenant_id", "entry_id" });
migrationBuilder.CreateIndex(
name: "ix_content_entries_created_by",
table: "content_entries",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_content_entries_tenant_id_entry_key",
table: "content_entries",
columns: new[] { "tenant_id", "entry_key" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_content_entries_tenant_id_legacy_id",
table: "content_entries",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_content_entries_tenant_id_region_id_entry_type_is_active_so~",
table: "content_entries",
columns: new[] { "tenant_id", "region_id", "entry_type", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_content_entries_updated_by",
table: "content_entries",
column: "updated_by");
migrationBuilder.CreateIndex(
name: "ix_content_nodes_created_by",
table: "content_nodes",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_content_nodes_path",
table: "content_nodes",
column: "path")
.Annotation("Npgsql:IndexMethod", "gist");
migrationBuilder.CreateIndex(
name: "ix_content_nodes_tenant_id_entry_id_node_key",
table: "content_nodes",
columns: new[] { "tenant_id", "entry_id", "node_key" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_content_nodes_tenant_id_entry_id_parent_id_sort_order",
table: "content_nodes",
columns: new[] { "tenant_id", "entry_id", "parent_id", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_content_nodes_tenant_id_legacy_id",
table: "content_nodes",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_content_nodes_tenant_id_marker_type",
table: "content_nodes",
columns: new[] { "tenant_id", "marker_type" },
filter: "marker_type is not null");
migrationBuilder.CreateIndex(
name: "ix_content_nodes_tenant_id_parent_id",
table: "content_nodes",
columns: new[] { "tenant_id", "parent_id" });
migrationBuilder.CreateIndex(
name: "ix_content_nodes_tenant_id_region_id",
table: "content_nodes",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_content_nodes_updated_by",
table: "content_nodes",
column: "updated_by");
migrationBuilder.CreateIndex(
name: "ix_practice_blueprints_created_by",
table: "practice_blueprints",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_practice_blueprints_tenant_id_collection_id",
table: "practice_blueprints",
columns: new[] { "tenant_id", "collection_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_blueprints_tenant_id_entry_id",
table: "practice_blueprints",
columns: new[] { "tenant_id", "entry_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_blueprints_tenant_id_legacy_id",
table: "practice_blueprints",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_practice_blueprints_tenant_id_node_id_collection_id_mode_st~",
table: "practice_blueprints",
columns: new[] { "tenant_id", "node_id", "collection_id", "mode", "status", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_practice_blueprints_tenant_id_region_id",
table: "practice_blueprints",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_blueprints_updated_by",
table: "practice_blueprints",
column: "updated_by");
migrationBuilder.CreateIndex(
name: "ix_question_collection_items_tenant_id_collection_id_question_~",
table: "question_collection_items",
columns: new[] { "tenant_id", "collection_id", "question_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_question_collection_items_tenant_id_collection_id_section_k~",
table: "question_collection_items",
columns: new[] { "tenant_id", "collection_id", "section_key", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_question_collection_items_tenant_id_question_id",
table: "question_collection_items",
columns: new[] { "tenant_id", "question_id" });
migrationBuilder.CreateIndex(
name: "ix_question_collections_created_by",
table: "question_collections",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_question_collections_tenant_id_category_id",
table: "question_collections",
columns: new[] { "tenant_id", "category_id" });
migrationBuilder.CreateIndex(
name: "ix_question_collections_tenant_id_entry_id",
table: "question_collections",
columns: new[] { "tenant_id", "entry_id" });
migrationBuilder.CreateIndex(
name: "ix_question_collections_tenant_id_legacy_id",
table: "question_collections",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_question_collections_tenant_id_node_id_status_sort_order",
table: "question_collections",
columns: new[] { "tenant_id", "node_id", "status", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_question_collections_tenant_id_question_bank_id",
table: "question_collections",
columns: new[] { "tenant_id", "question_bank_id" });
migrationBuilder.CreateIndex(
name: "ix_question_collections_tenant_id_region_id",
table: "question_collections",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_question_collections_tenant_id_subject_id",
table: "question_collections",
columns: new[] { "tenant_id", "subject_id" });
migrationBuilder.CreateIndex(
name: "ix_question_collections_updated_by",
table: "question_collections",
column: "updated_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_domains_host",
table: "tenant_domains",
column: "host",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_domains_tenant_id_is_primary",
table: "tenant_domains",
columns: new[] { "tenant_id", "is_primary" },
unique: true,
filter: "is_primary");
migrationBuilder.AddForeignKey(
name: "fk_practice_sessions_content_entries_tenant_id_entry_id",
table: "practice_sessions",
columns: new[] { "tenant_id", "entry_id" },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "fk_practice_sessions_content_nodes_tenant_id_content_node_id",
table: "practice_sessions",
columns: new[] { "tenant_id", "content_node_id" },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "fk_practice_sessions_practice_blueprints_tenant_id_blueprint_id",
table: "practice_sessions",
columns: new[] { "tenant_id", "blueprint_id" },
principalTable: "practice_blueprints",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "fk_practice_sessions_question_collections_tenant_id_collection~",
table: "practice_sessions",
columns: new[] { "tenant_id", "collection_id" },
principalTable: "question_collections",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "fk_questions_content_entries_tenant_id_entry_id",
table: "questions",
columns: new[] { "tenant_id", "entry_id" },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "fk_questions_content_nodes_tenant_id_content_node_id",
table: "questions",
columns: new[] { "tenant_id", "content_node_id" },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "fk_questions_question_collections_tenant_id_primary_collection~",
table: "questions",
columns: new[] { "tenant_id", "primary_collection_id" },
principalTable: "question_collections",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_practice_sessions_content_entries_tenant_id_entry_id",
table: "practice_sessions");
migrationBuilder.DropForeignKey(
name: "fk_practice_sessions_content_nodes_tenant_id_content_node_id",
table: "practice_sessions");
migrationBuilder.DropForeignKey(
name: "fk_practice_sessions_practice_blueprints_tenant_id_blueprint_id",
table: "practice_sessions");
migrationBuilder.DropForeignKey(
name: "fk_practice_sessions_question_collections_tenant_id_collection~",
table: "practice_sessions");
migrationBuilder.DropForeignKey(
name: "fk_questions_content_entries_tenant_id_entry_id",
table: "questions");
migrationBuilder.DropForeignKey(
name: "fk_questions_content_nodes_tenant_id_content_node_id",
table: "questions");
migrationBuilder.DropForeignKey(
name: "fk_questions_question_collections_tenant_id_primary_collection~",
table: "questions");
migrationBuilder.DropTable(
name: "practice_blueprints");
migrationBuilder.DropTable(
name: "question_collection_items");
migrationBuilder.DropTable(
name: "tenant_branding");
migrationBuilder.DropTable(
name: "tenant_domains");
migrationBuilder.DropTable(
name: "tenant_settings");
migrationBuilder.DropTable(
name: "question_collections");
migrationBuilder.DropTable(
name: "content_nodes");
migrationBuilder.DropTable(
name: "content_entries");
migrationBuilder.DropIndex(
name: "ix_questions_tenant_id_content_node_id",
table: "questions");
migrationBuilder.DropIndex(
name: "ix_questions_tenant_id_entry_id",
table: "questions");
migrationBuilder.DropIndex(
name: "ix_questions_tenant_id_primary_collection_id",
table: "questions");
migrationBuilder.DropIndex(
name: "ix_practice_sessions_tenant_id_blueprint_id",
table: "practice_sessions");
migrationBuilder.DropIndex(
name: "ix_practice_sessions_tenant_id_collection_id",
table: "practice_sessions");
migrationBuilder.DropIndex(
name: "ix_practice_sessions_tenant_id_content_node_id",
table: "practice_sessions");
migrationBuilder.DropIndex(
name: "ix_practice_sessions_tenant_id_entry_id",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "content_node_id",
table: "questions");
migrationBuilder.DropColumn(
name: "entry_id",
table: "questions");
migrationBuilder.DropColumn(
name: "exam_markers",
table: "questions");
migrationBuilder.DropColumn(
name: "primary_collection_id",
table: "questions");
migrationBuilder.DropColumn(
name: "blueprint_id",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "collection_id",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "content_node_id",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "duration_minutes",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "entry_id",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "expires_at",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "question_count",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "question_ids",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "total_score",
table: "practice_sessions");
migrationBuilder.AlterDatabase()
.Annotation("Npgsql:PostgresExtension:citext", ",,")
.OldAnnotation("Npgsql:PostgresExtension:citext", ",,")
.OldAnnotation("Npgsql:PostgresExtension:ltree", ",,");
}
}
}

View File

@@ -1,648 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddVocabularyAndHandbookPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "handbook_subjects",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
school_id = table.Column<Guid>(type: "uuid", nullable: true),
major_id = table.Column<Guid>(type: "uuid", nullable: true),
entry_id = table.Column<Guid>(type: "uuid", nullable: true),
content_node_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
icon = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
color = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
description = table.Column<string>(type: "text", nullable: true),
major_legacy_ids = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
source_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
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_handbook_subjects", x => x.id);
table.UniqueConstraint("ak_handbook_subjects_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_handbook_subjects_content_entries_tenant_id_entry_id",
columns: x => new { x.tenant_id, x.entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_handbook_subjects_content_nodes_tenant_id_content_node_id",
columns: x => new { x.tenant_id, x.content_node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_handbook_subjects_majors_tenant_id_major_id",
columns: x => new { x.tenant_id, x.major_id },
principalTable: "majors",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_handbook_subjects_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_handbook_subjects_schools_tenant_id_school_id",
columns: x => new { x.tenant_id, x.school_id },
principalTable: "schools",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_handbook_subjects_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "question_type_groups",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
subject_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
display_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
types = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
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_question_type_groups", x => x.id);
table.UniqueConstraint("ak_question_type_groups_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_question_type_groups_subjects_tenant_id_subject_id",
columns: x => new { x.tenant_id, x.subject_id },
principalTable: "subjects",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_question_type_groups_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "subject_shares",
columns: table => new
{
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
source_subject_id = table.Column<Guid>(type: "uuid", nullable: false),
target_subject_id = table.Column<Guid>(type: "uuid", nullable: false),
legacy_id = table.Column<string>(type: "text", nullable: true),
legacy_source_subject_id = table.Column<string>(type: "text", nullable: true),
legacy_target_subject_id = table.Column<string>(type: "text", nullable: true),
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)
},
constraints: table =>
{
table.PrimaryKey("pk_subject_shares", x => new { x.tenant_id, x.source_subject_id, x.target_subject_id });
table.ForeignKey(
name: "fk_subject_shares_subjects_tenant_id_source_subject_id",
columns: x => new { x.tenant_id, x.source_subject_id },
principalTable: "subjects",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_subject_shares_subjects_tenant_id_target_subject_id",
columns: x => new { x.tenant_id, x.target_subject_id },
principalTable: "subjects",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_subject_shares_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "vocabulary_units",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
entry_id = table.Column<Guid>(type: "uuid", nullable: true),
content_node_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
word_count = table.Column<int>(type: "integer", nullable: true),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
source_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
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_vocabulary_units", x => x.id);
table.UniqueConstraint("ak_vocabulary_units_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_vocabulary_units_content_entries_tenant_id_entry_id",
columns: x => new { x.tenant_id, x.entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_vocabulary_units_content_nodes_tenant_id_content_node_id",
columns: x => new { x.tenant_id, x.content_node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_vocabulary_units_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_vocabulary_units_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "handbook_chapters",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
subject_id = table.Column<Guid>(type: "uuid", nullable: true),
entry_id = table.Column<Guid>(type: "uuid", nullable: true),
content_node_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
source_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
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_handbook_chapters", x => x.id);
table.UniqueConstraint("ak_handbook_chapters_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_handbook_chapters_content_entries_tenant_id_entry_id",
columns: x => new { x.tenant_id, x.entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_handbook_chapters_content_nodes_tenant_id_content_node_id",
columns: x => new { x.tenant_id, x.content_node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_handbook_chapters_handbook_subjects_tenant_id_subject_id",
columns: x => new { x.tenant_id, x.subject_id },
principalTable: "handbook_subjects",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_handbook_chapters_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "vocabulary_words",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
unit_id = table.Column<Guid>(type: "uuid", nullable: true),
entry_id = table.Column<Guid>(type: "uuid", nullable: true),
content_node_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
word = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
phonetic = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
meaning = table.Column<string>(type: "text", nullable: true),
example = table.Column<string>(type: "text", nullable: true),
example_translation = table.Column<string>(type: "text", nullable: true),
difficulty = table.Column<int>(type: "integer", nullable: true),
tags = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
source_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
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_vocabulary_words", x => x.id);
table.UniqueConstraint("ak_vocabulary_words_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_vocabulary_words_content_entries_tenant_id_entry_id",
columns: x => new { x.tenant_id, x.entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_vocabulary_words_content_nodes_tenant_id_content_node_id",
columns: x => new { x.tenant_id, x.content_node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_vocabulary_words_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_vocabulary_words_vocabulary_units_tenant_id_unit_id",
columns: x => new { x.tenant_id, x.unit_id },
principalTable: "vocabulary_units",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "handbook_entries",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
chapter_id = table.Column<Guid>(type: "uuid", nullable: true),
entry_id = table.Column<Guid>(type: "uuid", nullable: true),
content_node_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
title = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
summary = table.Column<string>(type: "text", nullable: true),
content = table.Column<string>(type: "text", nullable: true),
tags = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
source_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
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_handbook_entries", x => x.id);
table.UniqueConstraint("ak_handbook_entries_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_handbook_entries_content_entries_tenant_id_entry_id",
columns: x => new { x.tenant_id, x.entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_handbook_entries_content_nodes_tenant_id_content_node_id",
columns: x => new { x.tenant_id, x.content_node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_handbook_entries_handbook_chapters_tenant_id_chapter_id",
columns: x => new { x.tenant_id, x.chapter_id },
principalTable: "handbook_chapters",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_handbook_entries_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "user_word_favorites",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
word_id = table.Column<Guid>(type: "uuid", nullable: false),
legacy_id = table.Column<string>(type: "text", nullable: true),
legacy_user_id = table.Column<string>(type: "text", nullable: true),
legacy_word_id = table.Column<string>(type: "text", nullable: true),
note = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
favorited_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_user_word_favorites", x => x.id);
table.UniqueConstraint("ak_user_word_favorites_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_user_word_favorites_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_user_word_favorites_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_user_word_favorites_vocabulary_words_tenant_id_word_id",
columns: x => new { x.tenant_id, x.word_id },
principalTable: "vocabulary_words",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "user_word_progress",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
word_id = table.Column<Guid>(type: "uuid", nullable: false),
legacy_id = table.Column<string>(type: "text", nullable: true),
legacy_user_id = table.Column<string>(type: "text", nullable: true),
legacy_word_id = table.Column<string>(type: "text", nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
correct_count = table.Column<int>(type: "integer", nullable: false),
wrong_count = table.Column<int>(type: "integer", nullable: false),
last_review_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
next_review_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
review_count = table.Column<int>(type: "integer", nullable: false),
correct_streak = table.Column<int>(type: "integer", nullable: false),
ease_factor = table.Column<decimal>(type: "numeric(4,2)", precision: 4, scale: 2, nullable: false, defaultValue: 2.50m),
last_result = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
due_level = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
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_user_word_progress", x => x.id);
table.UniqueConstraint("ak_user_word_progress_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0");
table.CheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00");
table.CheckConstraint("ck_user_word_progress_review_count", "review_count >= 0");
table.ForeignKey(
name: "fk_user_word_progress_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_user_word_progress_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_user_word_progress_vocabulary_words_tenant_id_word_id",
columns: x => new { x.tenant_id, x.word_id },
principalTable: "vocabulary_words",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_handbook_chapters_tenant_id_content_node_id",
table: "handbook_chapters",
columns: new[] { "tenant_id", "content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_handbook_chapters_tenant_id_entry_id_content_node_id_subjec~",
table: "handbook_chapters",
columns: new[] { "tenant_id", "entry_id", "content_node_id", "subject_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_handbook_chapters_tenant_id_legacy_id",
table: "handbook_chapters",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_handbook_chapters_tenant_id_subject_id",
table: "handbook_chapters",
columns: new[] { "tenant_id", "subject_id" });
migrationBuilder.CreateIndex(
name: "ix_handbook_entries_tenant_id_chapter_id",
table: "handbook_entries",
columns: new[] { "tenant_id", "chapter_id" });
migrationBuilder.CreateIndex(
name: "ix_handbook_entries_tenant_id_content_node_id",
table: "handbook_entries",
columns: new[] { "tenant_id", "content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_handbook_entries_tenant_id_entry_id_content_node_id_chapter~",
table: "handbook_entries",
columns: new[] { "tenant_id", "entry_id", "content_node_id", "chapter_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_handbook_entries_tenant_id_legacy_id",
table: "handbook_entries",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_handbook_subjects_tenant_id_content_node_id",
table: "handbook_subjects",
columns: new[] { "tenant_id", "content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_handbook_subjects_tenant_id_entry_id_content_node_id_region~",
table: "handbook_subjects",
columns: new[] { "tenant_id", "entry_id", "content_node_id", "region_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_handbook_subjects_tenant_id_legacy_id",
table: "handbook_subjects",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_handbook_subjects_tenant_id_major_id",
table: "handbook_subjects",
columns: new[] { "tenant_id", "major_id" });
migrationBuilder.CreateIndex(
name: "ix_handbook_subjects_tenant_id_region_id",
table: "handbook_subjects",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_handbook_subjects_tenant_id_school_id",
table: "handbook_subjects",
columns: new[] { "tenant_id", "school_id" });
migrationBuilder.CreateIndex(
name: "ix_question_type_groups_tenant_id_legacy_id",
table: "question_type_groups",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_question_type_groups_tenant_id_subject_id_is_active_sort_or~",
table: "question_type_groups",
columns: new[] { "tenant_id", "subject_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_subject_shares_tenant_id_target_subject_id",
table: "subject_shares",
columns: new[] { "tenant_id", "target_subject_id" });
migrationBuilder.CreateIndex(
name: "ix_user_word_favorites_tenant_id_user_id_word_id",
table: "user_word_favorites",
columns: new[] { "tenant_id", "user_id", "word_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_user_word_favorites_tenant_id_word_id",
table: "user_word_favorites",
columns: new[] { "tenant_id", "word_id" });
migrationBuilder.CreateIndex(
name: "ix_user_word_favorites_user_id",
table: "user_word_favorites",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_user_word_progress_tenant_id_user_id_next_review_at_status_~",
table: "user_word_progress",
columns: new[] { "tenant_id", "user_id", "next_review_at", "status", "due_level" });
migrationBuilder.CreateIndex(
name: "ix_user_word_progress_tenant_id_user_id_word_id",
table: "user_word_progress",
columns: new[] { "tenant_id", "user_id", "word_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_user_word_progress_tenant_id_word_id",
table: "user_word_progress",
columns: new[] { "tenant_id", "word_id" });
migrationBuilder.CreateIndex(
name: "ix_user_word_progress_user_id",
table: "user_word_progress",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_vocabulary_units_tenant_id_content_node_id",
table: "vocabulary_units",
columns: new[] { "tenant_id", "content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_vocabulary_units_tenant_id_entry_id_content_node_id_region_~",
table: "vocabulary_units",
columns: new[] { "tenant_id", "entry_id", "content_node_id", "region_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_vocabulary_units_tenant_id_legacy_id",
table: "vocabulary_units",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_vocabulary_units_tenant_id_region_id",
table: "vocabulary_units",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_vocabulary_words_tenant_id_content_node_id",
table: "vocabulary_words",
columns: new[] { "tenant_id", "content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_vocabulary_words_tenant_id_entry_id_content_node_id_unit_id~",
table: "vocabulary_words",
columns: new[] { "tenant_id", "entry_id", "content_node_id", "unit_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_vocabulary_words_tenant_id_legacy_id",
table: "vocabulary_words",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_vocabulary_words_tenant_id_unit_id_is_active_sort_order_cre~",
table: "vocabulary_words",
columns: new[] { "tenant_id", "unit_id", "is_active", "sort_order", "created_at" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "handbook_entries");
migrationBuilder.DropTable(
name: "question_type_groups");
migrationBuilder.DropTable(
name: "subject_shares");
migrationBuilder.DropTable(
name: "user_word_favorites");
migrationBuilder.DropTable(
name: "user_word_progress");
migrationBuilder.DropTable(
name: "handbook_chapters");
migrationBuilder.DropTable(
name: "vocabulary_words");
migrationBuilder.DropTable(
name: "handbook_subjects");
migrationBuilder.DropTable(
name: "vocabulary_units");
}
}
}

View File

@@ -1,630 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddContentAssetsAndImportPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "app_assets",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
asset_key = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
file_name = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
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_app_assets", x => x.id);
table.UniqueConstraint("ak_app_assets_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_app_assets_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "content_assets",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
subject_id = table.Column<Guid>(type: "uuid", nullable: true),
category_id = table.Column<Guid>(type: "uuid", nullable: true),
content_node_id = table.Column<Guid>(type: "uuid", nullable: true),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
verified_by = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
asset_key = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
category = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
description = table.Column<string>(type: "text", nullable: true),
file_name = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
cdn_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
is_public = table.Column<bool>(type: "boolean", nullable: false),
asset_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
storage_provider = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
bucket = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
object_key = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
mime_type = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
file_size_bytes = table.Column<long>(type: "bigint", nullable: true),
checksum_sha256 = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
visibility = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
preview_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
access_rules = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
source = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
download_count = table.Column<int>(type: "integer", nullable: false),
upload_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
verified_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
verified_size_bytes = table.Column<long>(type: "bigint", nullable: true),
verified_checksum_sha256 = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
verification_details = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
preview_object_key = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
preview_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
security_flags = 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_content_assets", x => x.id);
table.UniqueConstraint("ak_content_assets_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_content_assets_download_count", "download_count >= 0");
table.CheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0");
table.CheckConstraint("ck_content_assets_verified_checksum", "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'");
table.CheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0");
table.ForeignKey(
name: "fk_content_assets_categories_tenant_id_category_id",
columns: x => new { x.tenant_id, x.category_id },
principalTable: "categories",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_assets_content_nodes_tenant_id_content_node_id",
columns: x => new { x.tenant_id, x.content_node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_assets_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_content_assets_subjects_tenant_id_subject_id",
columns: x => new { x.tenant_id, x.subject_id },
principalTable: "subjects",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_assets_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_assets_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_content_assets_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_content_assets_users_verified_by",
column: x => x.verified_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "content_import_jobs",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
target_region_id = table.Column<Guid>(type: "uuid", nullable: true),
target_subject_id = table.Column<Guid>(type: "uuid", nullable: true),
target_category_id = table.Column<Guid>(type: "uuid", nullable: true),
target_content_node_id = table.Column<Guid>(type: "uuid", nullable: true),
target_question_bank_id = table.Column<Guid>(type: "uuid", nullable: true),
import_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
source_format = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
source_name = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
source_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
dry_run = table.Column<bool>(type: "boolean", nullable: false),
total_count = table.Column<int>(type: "integer", nullable: false),
valid_count = table.Column<int>(type: "integer", nullable: false),
error_count = table.Column<int>(type: "integer", nullable: false),
warning_count = table.Column<int>(type: "integer", nullable: false),
inserted_count = table.Column<int>(type: "integer", nullable: false),
updated_count = table.Column<int>(type: "integer", nullable: false),
skipped_count = table.Column<int>(type: "integer", nullable: false),
summary = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
raw_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
normalized_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
error_message = table.Column<string>(type: "text", nullable: true),
started_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
finished_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_content_import_jobs", x => x.id);
table.UniqueConstraint("ak_content_import_jobs_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0");
table.ForeignKey(
name: "fk_content_import_jobs_categories_tenant_id_target_category_id",
columns: x => new { x.tenant_id, x.target_category_id },
principalTable: "categories",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_import_jobs_content_nodes_tenant_id_target_content_~",
columns: x => new { x.tenant_id, x.target_content_node_id },
principalTable: "content_nodes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_import_jobs_question_banks_tenant_id_target_questio~",
columns: x => new { x.tenant_id, x.target_question_bank_id },
principalTable: "question_banks",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_import_jobs_regions_tenant_id_target_region_id",
columns: x => new { x.tenant_id, x.target_region_id },
principalTable: "regions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_import_jobs_subjects_tenant_id_target_subject_id",
columns: x => new { x.tenant_id, x.target_subject_id },
principalTable: "subjects",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_import_jobs_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_import_jobs_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "images",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
category = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
file_name = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
is_public = table.Column<bool>(type: "boolean", nullable: false),
cdn_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
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_images", x => x.id);
table.UniqueConstraint("ak_images_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_images_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "video_explanations",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
subject_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_subject_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
video_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
thumbnail_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
duration_seconds = table.Column<int>(type: "integer", nullable: true),
knowledge_tags = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
is_general = table.Column<bool>(type: "boolean", nullable: false),
difficulty = table.Column<int>(type: "integer", nullable: true),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
source_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
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_video_explanations", x => x.id);
table.UniqueConstraint("ak_video_explanations_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_video_explanations_subjects_tenant_id_subject_id",
columns: x => new { x.tenant_id, x.subject_id },
principalTable: "subjects",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_video_explanations_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "content_import_items",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
job_id = table.Column<Guid>(type: "uuid", nullable: false),
row_no = table.Column<int>(type: "integer", nullable: false),
external_id = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
target_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
target_id = table.Column<Guid>(type: "uuid", nullable: true),
source_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
normalized_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
content_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
issues_count = table.Column<int>(type: "integer", nullable: false),
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_content_import_items", x => x.id);
table.UniqueConstraint("ak_content_import_items_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0");
table.CheckConstraint("ck_content_import_items_row_no", "row_no >= 0");
table.ForeignKey(
name: "fk_content_import_items_content_import_jobs_tenant_id_job_id",
columns: x => new { x.tenant_id, x.job_id },
principalTable: "content_import_jobs",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_import_items_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "question_videos",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
question_id = table.Column<Guid>(type: "uuid", nullable: true),
video_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_question_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_video_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
video_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
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_question_videos", x => x.id);
table.UniqueConstraint("ak_question_videos_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_question_videos_questions_tenant_id_question_id",
columns: x => new { x.tenant_id, x.question_id },
principalTable: "questions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_question_videos_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_question_videos_video_explanations_tenant_id_video_id",
columns: x => new { x.tenant_id, x.video_id },
principalTable: "video_explanations",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "content_import_issues",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
job_id = table.Column<Guid>(type: "uuid", nullable: false),
item_id = table.Column<Guid>(type: "uuid", nullable: true),
row_no = table.Column<int>(type: "integer", nullable: true),
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
field_path = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
message = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: false),
details = 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_content_import_issues", x => x.id);
table.UniqueConstraint("ak_content_import_issues_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_content_import_issues_content_import_items_tenant_id_item_id",
columns: x => new { x.tenant_id, x.item_id },
principalTable: "content_import_items",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_import_issues_content_import_jobs_tenant_id_job_id",
columns: x => new { x.tenant_id, x.job_id },
principalTable: "content_import_jobs",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_import_issues_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_app_assets_tenant_id_asset_key",
table: "app_assets",
columns: new[] { "tenant_id", "asset_key" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_app_assets_tenant_id_legacy_id",
table: "app_assets",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_content_assets_created_by",
table: "content_assets",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_category_id",
table: "content_assets",
columns: new[] { "tenant_id", "category_id" });
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_checksum_sha256",
table: "content_assets",
columns: new[] { "tenant_id", "checksum_sha256" },
filter: "checksum_sha256 is not null");
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_content_node_id",
table: "content_assets",
columns: new[] { "tenant_id", "content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_legacy_id",
table: "content_assets",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_preview_status",
table: "content_assets",
columns: new[] { "tenant_id", "preview_status" },
filter: "preview_status <> 'none'");
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_region_id",
table: "content_assets",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_status_visibility_asset_type_regio~",
table: "content_assets",
columns: new[] { "tenant_id", "status", "visibility", "asset_type", "region_id", "subject_id", "category_id", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_storage_provider_bucket_object_key",
table: "content_assets",
columns: new[] { "tenant_id", "storage_provider", "bucket", "object_key" });
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_subject_id",
table: "content_assets",
columns: new[] { "tenant_id", "subject_id" });
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_upload_status_status_updated_at",
table: "content_assets",
columns: new[] { "tenant_id", "upload_status", "status", "updated_at" });
migrationBuilder.CreateIndex(
name: "ix_content_assets_updated_by",
table: "content_assets",
column: "updated_by");
migrationBuilder.CreateIndex(
name: "ix_content_assets_verified_by",
table: "content_assets",
column: "verified_by");
migrationBuilder.CreateIndex(
name: "ix_content_import_issues_tenant_id_item_id",
table: "content_import_issues",
columns: new[] { "tenant_id", "item_id" });
migrationBuilder.CreateIndex(
name: "ix_content_import_issues_tenant_id_job_id_severity_row_no",
table: "content_import_issues",
columns: new[] { "tenant_id", "job_id", "severity", "row_no" });
migrationBuilder.CreateIndex(
name: "ix_content_import_items_job_id_row_no",
table: "content_import_items",
columns: new[] { "job_id", "row_no" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_content_import_items_tenant_id_job_id_status_row_no",
table: "content_import_items",
columns: new[] { "tenant_id", "job_id", "status", "row_no" });
migrationBuilder.CreateIndex(
name: "ix_content_import_jobs_created_by",
table: "content_import_jobs",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_content_import_jobs_tenant_id_import_type_status_created_at",
table: "content_import_jobs",
columns: new[] { "tenant_id", "import_type", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_content_import_jobs_tenant_id_target_category_id",
table: "content_import_jobs",
columns: new[] { "tenant_id", "target_category_id" });
migrationBuilder.CreateIndex(
name: "ix_content_import_jobs_tenant_id_target_content_node_id",
table: "content_import_jobs",
columns: new[] { "tenant_id", "target_content_node_id" });
migrationBuilder.CreateIndex(
name: "ix_content_import_jobs_tenant_id_target_question_bank_id",
table: "content_import_jobs",
columns: new[] { "tenant_id", "target_question_bank_id" });
migrationBuilder.CreateIndex(
name: "ix_content_import_jobs_tenant_id_target_region_id",
table: "content_import_jobs",
columns: new[] { "tenant_id", "target_region_id" });
migrationBuilder.CreateIndex(
name: "ix_content_import_jobs_tenant_id_target_subject_id",
table: "content_import_jobs",
columns: new[] { "tenant_id", "target_subject_id" });
migrationBuilder.CreateIndex(
name: "ix_images_tenant_id_category_is_public",
table: "images",
columns: new[] { "tenant_id", "category", "is_public" });
migrationBuilder.CreateIndex(
name: "ix_images_tenant_id_legacy_id",
table: "images",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_question_videos_tenant_id_legacy_id",
table: "question_videos",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_question_videos_tenant_id_question_id_video_id_video_type",
table: "question_videos",
columns: new[] { "tenant_id", "question_id", "video_id", "video_type" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_question_videos_tenant_id_video_id",
table: "question_videos",
columns: new[] { "tenant_id", "video_id" });
migrationBuilder.CreateIndex(
name: "ix_video_explanations_tenant_id_legacy_id",
table: "video_explanations",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_video_explanations_tenant_id_source_hash",
table: "video_explanations",
columns: new[] { "tenant_id", "source_hash" },
filter: "source_hash is not null");
migrationBuilder.CreateIndex(
name: "ix_video_explanations_tenant_id_subject_id_is_active_sort_order",
table: "video_explanations",
columns: new[] { "tenant_id", "subject_id", "is_active", "sort_order" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "app_assets");
migrationBuilder.DropTable(
name: "content_assets");
migrationBuilder.DropTable(
name: "content_import_issues");
migrationBuilder.DropTable(
name: "images");
migrationBuilder.DropTable(
name: "question_videos");
migrationBuilder.DropTable(
name: "content_import_items");
migrationBuilder.DropTable(
name: "video_explanations");
migrationBuilder.DropTable(
name: "content_import_jobs");
}
}
}

View File

@@ -1,764 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddLearningReportsAndPracticeAccessPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<JsonElement>(
name: "access_rules",
table: "question_collections",
type: "jsonb",
nullable: false,
defaultValueSql: "'{}'::jsonb");
migrationBuilder.AddColumn<Guid>(
name: "access_entitlement_id",
table: "practice_sessions",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "access_mode",
table: "practice_sessions",
type: "character varying(32)",
maxLength: 32,
nullable: false,
defaultValue: "free");
migrationBuilder.AddColumn<JsonElement>(
name: "access_snapshot",
table: "practice_sessions",
type: "jsonb",
nullable: false,
defaultValueSql: "'{}'::jsonb");
migrationBuilder.AddColumn<int>(
name: "consumed_free_quota",
table: "practice_sessions",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<JsonElement>(
name: "access_rules",
table: "practice_blueprints",
type: "jsonb",
nullable: false,
defaultValueSql: "'{}'::jsonb");
migrationBuilder.AddColumn<JsonElement>(
name: "access_rules",
table: "content_nodes",
type: "jsonb",
nullable: false,
defaultValueSql: "'{}'::jsonb");
migrationBuilder.CreateTable(
name: "dashboard_daily_stats",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_region_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
stat_date = table.Column<DateOnly>(type: "date", nullable: false),
new_users = table.Column<int>(type: "integer", nullable: false),
new_questions = table.Column<int>(type: "integer", nullable: false),
new_orders = table.Column<int>(type: "integer", nullable: false),
new_revenue_cents = table.Column<int>(type: "integer", nullable: false),
active_users = table.Column<int>(type: "integer", nullable: false),
rebuilt_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_dashboard_daily_stats", x => x.id);
table.UniqueConstraint("ak_dashboard_daily_stats_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_dashboard_daily_stats_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_dashboard_daily_stats_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "exam_dates",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
school_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
exam_name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
exam_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
exam_type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
description = table.Column<string>(type: "text", nullable: true),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
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_exam_dates", x => x.id);
table.UniqueConstraint("ak_exam_dates_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_exam_dates_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_exam_dates_schools_tenant_id_school_id",
columns: x => new { x.tenant_id, x.school_id },
principalTable: "schools",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_exam_dates_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "practice_access_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
user_id = table.Column<Guid>(type: "uuid", nullable: true),
practice_session_id = table.Column<Guid>(type: "uuid", nullable: true),
event_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
access_mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
requested_count = table.Column<int>(type: "integer", nullable: false),
granted_count = table.Column<int>(type: "integer", nullable: false),
consumed_free_quota = table.Column<int>(type: "integer", nullable: false),
reason = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
scope_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
scope_id = table.Column<Guid>(type: "uuid", nullable: true),
entitlement_id = table.Column<Guid>(type: "uuid", nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_practice_access_events", x => x.id);
table.UniqueConstraint("ak_practice_access_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0");
table.CheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0");
table.CheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0");
table.ForeignKey(
name: "fk_practice_access_events_practice_sessions_tenant_id_practice~",
columns: x => new { x.tenant_id, x.practice_session_id },
principalTable: "practice_sessions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_practice_access_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_practice_access_events_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "practice_daily_usage",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
usage_date = table.Column<DateOnly>(type: "date", nullable: false, defaultValueSql: "current_date"),
scope_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
scope_id = table.Column<Guid>(type: "uuid", nullable: true),
free_limit = table.Column<int>(type: "integer", nullable: false),
used_count = table.Column<int>(type: "integer", nullable: false),
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_practice_daily_usage", x => x.id);
table.UniqueConstraint("ak_practice_daily_usage_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0");
table.CheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0");
table.ForeignKey(
name: "fk_practice_daily_usage_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_practice_daily_usage_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "practice_session_reports",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
practice_session_id = table.Column<Guid>(type: "uuid", nullable: false),
blueprint_id = table.Column<Guid>(type: "uuid", nullable: true),
collection_id = table.Column<Guid>(type: "uuid", nullable: true),
mode = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
total_questions = table.Column<int>(type: "integer", nullable: false),
answered_count = table.Column<int>(type: "integer", nullable: false),
correct_count = table.Column<int>(type: "integer", nullable: false),
wrong_count = table.Column<int>(type: "integer", nullable: false),
unanswered_count = table.Column<int>(type: "integer", nullable: false),
score = table.Column<decimal>(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false),
total_score = table.Column<decimal>(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false),
accuracy = table.Column<decimal>(type: "numeric(6,4)", precision: 6, scale: 4, nullable: false),
duration_seconds = table.Column<int>(type: "integer", nullable: false),
started_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
submitted_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
section_stats = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
question_results = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
wrong_question_ids = 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_practice_session_reports", x => x.id);
table.UniqueConstraint("ak_practice_session_reports_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1");
table.CheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0");
table.CheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0");
table.CheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0");
table.ForeignKey(
name: "fk_practice_session_reports_practice_blueprints_tenant_id_blue~",
columns: x => new { x.tenant_id, x.blueprint_id },
principalTable: "practice_blueprints",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_practice_session_reports_practice_sessions_tenant_id_practi~",
columns: x => new { x.tenant_id, x.practice_session_id },
principalTable: "practice_sessions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_practice_session_reports_question_collections_tenant_id_col~",
columns: x => new { x.tenant_id, x.collection_id },
principalTable: "question_collections",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_practice_session_reports_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_practice_session_reports_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "reports",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
question_id = table.Column<Guid>(type: "uuid", nullable: true),
user_id = table.Column<Guid>(type: "uuid", nullable: true),
handled_by = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
category = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
description = table.Column<string>(type: "text", nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
priority = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
handled_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
resolution = table.Column<string>(type: "text", nullable: true),
contact = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
attachments = 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_reports", x => x.id);
table.UniqueConstraint("ak_reports_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_reports_questions_tenant_id_question_id",
columns: x => new { x.tenant_id, x.question_id },
principalTable: "questions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_reports_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_reports_users_handled_by",
column: x => x.handled_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_reports_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "revenue_daily_stats",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_region_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
stat_date = table.Column<DateOnly>(type: "date", nullable: false),
sale_type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
real_revenue_cents = table.Column<int>(type: "integer", nullable: false),
order_count = table.Column<int>(type: "integer", nullable: false),
code_count = table.Column<int>(type: "integer", nullable: false),
code_used = table.Column<int>(type: "integer", nullable: false),
code_estimated = table.Column<int>(type: "integer", nullable: false),
estimated_revenue_cents = table.Column<int>(type: "integer", nullable: false),
rebuilt_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_revenue_daily_stats", x => x.id);
table.UniqueConstraint("ak_revenue_daily_stats_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_revenue_daily_stats_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_revenue_daily_stats_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "user_score_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
event_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
points = table.Column<int>(type: "integer", nullable: false),
balance_after = table.Column<int>(type: "integer", nullable: false),
source_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
source_id = table.Column<Guid>(type: "uuid", nullable: true),
idempotency_key = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_user_score_events", x => x.id);
table.UniqueConstraint("ak_user_score_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_user_score_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_user_score_events_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "practice_session_report_sections",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
report_id = table.Column<Guid>(type: "uuid", nullable: false),
practice_session_id = table.Column<Guid>(type: "uuid", nullable: false),
section_key = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
section_name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
question_type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
question_count = table.Column<int>(type: "integer", nullable: false),
answered_count = table.Column<int>(type: "integer", nullable: false),
correct_count = table.Column<int>(type: "integer", nullable: false),
wrong_count = table.Column<int>(type: "integer", nullable: false),
unanswered_count = table.Column<int>(type: "integer", nullable: false),
score = table.Column<decimal>(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false),
total_score = table.Column<decimal>(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false),
accuracy = table.Column<decimal>(type: "numeric(6,4)", precision: 6, scale: 4, nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
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_practice_session_report_sections", x => x.id);
table.UniqueConstraint("ak_practice_session_report_sections_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1");
table.CheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0");
table.CheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0");
table.ForeignKey(
name: "fk_practice_session_report_sections_practice_session_reports_t~",
columns: x => new { x.tenant_id, x.report_id },
principalTable: "practice_session_reports",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_practice_session_report_sections_practice_sessions_tenant_i~",
columns: x => new { x.tenant_id, x.practice_session_id },
principalTable: "practice_sessions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_practice_session_report_sections_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "report_status_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
report_id = table.Column<Guid>(type: "uuid", nullable: false),
from_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
to_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
note = table.Column<string>(type: "text", nullable: true),
actor_user_id = table.Column<Guid>(type: "uuid", nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_report_status_events", x => x.id);
table.UniqueConstraint("ak_report_status_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_report_status_events_reports_tenant_id_report_id",
columns: x => new { x.tenant_id, x.report_id },
principalTable: "reports",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_report_status_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_report_status_events_users_actor_user_id",
column: x => x.actor_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.AddCheckConstraint(
name: "ck_practice_sessions_consumed_free_quota",
table: "practice_sessions",
sql: "consumed_free_quota >= 0");
migrationBuilder.CreateIndex(
name: "ix_dashboard_daily_stats_tenant_id_legacy_id",
table: "dashboard_daily_stats",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_dashboard_daily_stats_tenant_id_region_id",
table: "dashboard_daily_stats",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_dashboard_daily_stats_tenant_id_stat_date_region_id",
table: "dashboard_daily_stats",
columns: new[] { "tenant_id", "stat_date", "region_id" },
unique: true)
.Annotation("Npgsql:NullsDistinct", false);
migrationBuilder.CreateIndex(
name: "ix_exam_dates_tenant_id_legacy_id",
table: "exam_dates",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_exam_dates_tenant_id_region_id",
table: "exam_dates",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_exam_dates_tenant_id_school_id_exam_at",
table: "exam_dates",
columns: new[] { "tenant_id", "school_id", "exam_at" });
migrationBuilder.CreateIndex(
name: "ix_practice_access_events_tenant_id_practice_session_id",
table: "practice_access_events",
columns: new[] { "tenant_id", "practice_session_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_access_events_tenant_id_user_id_created_at",
table: "practice_access_events",
columns: new[] { "tenant_id", "user_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_practice_access_events_user_id",
table: "practice_access_events",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_practice_daily_usage_tenant_id_user_id_usage_date",
table: "practice_daily_usage",
columns: new[] { "tenant_id", "user_id", "usage_date" });
migrationBuilder.CreateIndex(
name: "ix_practice_daily_usage_tenant_id_user_id_usage_date_scope_typ~",
table: "practice_daily_usage",
columns: new[] { "tenant_id", "user_id", "usage_date", "scope_type", "scope_id" },
unique: true)
.Annotation("Npgsql:NullsDistinct", false);
migrationBuilder.CreateIndex(
name: "ix_practice_daily_usage_user_id",
table: "practice_daily_usage",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_practice_session_report_sections_tenant_id_practice_session~",
table: "practice_session_report_sections",
columns: new[] { "tenant_id", "practice_session_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_session_report_sections_tenant_id_report_id_sectio~",
table: "practice_session_report_sections",
columns: new[] { "tenant_id", "report_id", "section_key" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_practice_session_report_sections_tenant_id_report_id_sort_o~",
table: "practice_session_report_sections",
columns: new[] { "tenant_id", "report_id", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_practice_session_reports_tenant_id_blueprint_id_submitted_at",
table: "practice_session_reports",
columns: new[] { "tenant_id", "blueprint_id", "submitted_at" },
filter: "blueprint_id is not null");
migrationBuilder.CreateIndex(
name: "ix_practice_session_reports_tenant_id_collection_id",
table: "practice_session_reports",
columns: new[] { "tenant_id", "collection_id" });
migrationBuilder.CreateIndex(
name: "ix_practice_session_reports_tenant_id_practice_session_id",
table: "practice_session_reports",
columns: new[] { "tenant_id", "practice_session_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_practice_session_reports_tenant_id_user_id_submitted_at",
table: "practice_session_reports",
columns: new[] { "tenant_id", "user_id", "submitted_at" });
migrationBuilder.CreateIndex(
name: "ix_practice_session_reports_user_id",
table: "practice_session_reports",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_report_status_events_actor_user_id",
table: "report_status_events",
column: "actor_user_id");
migrationBuilder.CreateIndex(
name: "ix_report_status_events_tenant_id_report_id_created_at",
table: "report_status_events",
columns: new[] { "tenant_id", "report_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_reports_handled_by",
table: "reports",
column: "handled_by");
migrationBuilder.CreateIndex(
name: "ix_reports_tenant_id_legacy_id",
table: "reports",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_reports_tenant_id_question_id_created_at",
table: "reports",
columns: new[] { "tenant_id", "question_id", "created_at" },
filter: "question_id is not null");
migrationBuilder.CreateIndex(
name: "ix_reports_tenant_id_status_created_at",
table: "reports",
columns: new[] { "tenant_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_reports_user_id",
table: "reports",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_revenue_daily_stats_tenant_id_legacy_id",
table: "revenue_daily_stats",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_revenue_daily_stats_tenant_id_region_id",
table: "revenue_daily_stats",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_revenue_daily_stats_tenant_id_stat_date_region_id_sale_type",
table: "revenue_daily_stats",
columns: new[] { "tenant_id", "stat_date", "region_id", "sale_type" },
unique: true)
.Annotation("Npgsql:NullsDistinct", false);
migrationBuilder.CreateIndex(
name: "ix_user_score_events_tenant_id_idempotency_key",
table: "user_score_events",
columns: new[] { "tenant_id", "idempotency_key" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_user_score_events_tenant_id_user_id_created_at",
table: "user_score_events",
columns: new[] { "tenant_id", "user_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_user_score_events_user_id",
table: "user_score_events",
column: "user_id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "dashboard_daily_stats");
migrationBuilder.DropTable(
name: "exam_dates");
migrationBuilder.DropTable(
name: "practice_access_events");
migrationBuilder.DropTable(
name: "practice_daily_usage");
migrationBuilder.DropTable(
name: "practice_session_report_sections");
migrationBuilder.DropTable(
name: "report_status_events");
migrationBuilder.DropTable(
name: "revenue_daily_stats");
migrationBuilder.DropTable(
name: "user_score_events");
migrationBuilder.DropTable(
name: "practice_session_reports");
migrationBuilder.DropTable(
name: "reports");
migrationBuilder.DropCheckConstraint(
name: "ck_practice_sessions_consumed_free_quota",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "access_rules",
table: "question_collections");
migrationBuilder.DropColumn(
name: "access_entitlement_id",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "access_mode",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "access_snapshot",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "consumed_free_quota",
table: "practice_sessions");
migrationBuilder.DropColumn(
name: "access_rules",
table: "practice_blueprints");
migrationBuilder.DropColumn(
name: "access_rules",
table: "content_nodes");
}
}
}

View File

@@ -1,669 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddAuthAndTenantOperationsPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "role_template_id",
table: "tenant_memberships",
type: "uuid",
nullable: true);
migrationBuilder.CreateTable(
name: "auth_login_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
user_id = table.Column<Guid>(type: "uuid", nullable: true),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
identifier = table.Column<string>(type: "character varying(320)", maxLength: 320, nullable: true),
result = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
failure_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
ip_address = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
user_agent = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_auth_login_events", x => x.id);
table.UniqueConstraint("ak_auth_login_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_auth_login_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_auth_login_events_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "auth_sessions",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
token_hash = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
revoked_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
ip_address = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
user_agent = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
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_auth_sessions", x => x.id);
table.UniqueConstraint("ak_auth_sessions_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_auth_sessions_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_auth_sessions_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "sms_send_rate_limits",
columns: table => new
{
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
dimension = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
scope_hash = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
bucket_start = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
request_count = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_sms_send_rate_limits", x => new { x.tenant_id, x.dimension, x.scope_hash, x.bucket_start });
table.CheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0");
table.ForeignKey(
name: "fk_sms_send_rate_limits_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "sms_verification_codes",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
phone = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
purpose = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
code_hash = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
attempts = table.Column<int>(type: "integer", nullable: false),
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
consumed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
ip_address = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
user_agent = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_sms_verification_codes", x => x.id);
table.UniqueConstraint("ak_sms_verification_codes_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0");
table.ForeignKey(
name: "fk_sms_verification_codes_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_auth_providers",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
display_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
config_public = 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_tenant_auth_providers", x => x.id);
table.UniqueConstraint("ak_tenant_auth_providers_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_auth_providers_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_classes",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_tenant_classes", x => x.id);
table.UniqueConstraint("ak_tenant_classes_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_classes_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_tenant_classes_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_classes_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_classes_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "tenant_role_templates",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
base_role = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
menu_permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
module_permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
field_permissions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
data_scope = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
is_system = table.Column<bool>(type: "boolean", nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_tenant_role_templates", x => x.id);
table.UniqueConstraint("ak_tenant_role_templates_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_role_templates_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_role_templates_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_role_templates_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "tenant_student_notes",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
student_user_id = table.Column<Guid>(type: "uuid", nullable: false),
note_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
content = table.Column<string>(type: "text", nullable: false),
visibility = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
is_pinned = table.Column<bool>(type: "boolean", nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_tenant_student_notes", x => x.id);
table.UniqueConstraint("ak_tenant_student_notes_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_student_notes_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_student_notes_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_student_notes_users_student_user_id",
column: x => x.student_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_student_notes_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "tenant_class_members",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
class_id = table.Column<Guid>(type: "uuid", nullable: false),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
member_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
joined_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
left_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_tenant_class_members", x => x.id);
table.UniqueConstraint("ak_tenant_class_members_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_class_members_tenant_classes_tenant_id_class_id",
columns: x => new { x.tenant_id, x.class_id },
principalTable: "tenant_classes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_class_members_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_class_members_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_student_followups",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
student_user_id = table.Column<Guid>(type: "uuid", nullable: false),
assigned_to_user_id = table.Column<Guid>(type: "uuid", nullable: true),
class_id = table.Column<Guid>(type: "uuid", nullable: true),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
followup_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
priority = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
due_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
completed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
completed_by = table.Column<Guid>(type: "uuid", nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_tenant_student_followups", x => x.id);
table.UniqueConstraint("ak_tenant_student_followups_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_student_followups_tenant_classes_tenant_id_class_id",
columns: x => new { x.tenant_id, x.class_id },
principalTable: "tenant_classes",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_student_followups_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_student_followups_users_assigned_to_user_id",
column: x => x.assigned_to_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_student_followups_users_completed_by",
column: x => x.completed_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_student_followups_users_student_user_id",
column: x => x.student_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_tenant_memberships_tenant_id_role_template_id",
table: "tenant_memberships",
columns: new[] { "tenant_id", "role_template_id" });
migrationBuilder.CreateIndex(
name: "ix_auth_login_events_tenant_id_user_id_created_at",
table: "auth_login_events",
columns: new[] { "tenant_id", "user_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_auth_login_events_user_id",
table: "auth_login_events",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_auth_sessions_tenant_id_user_id_expires_at",
table: "auth_sessions",
columns: new[] { "tenant_id", "user_id", "expires_at" },
filter: "revoked_at is null");
migrationBuilder.CreateIndex(
name: "ix_auth_sessions_token_hash",
table: "auth_sessions",
column: "token_hash",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_auth_sessions_user_id",
table: "auth_sessions",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_sms_send_rate_limits_updated_at",
table: "sms_send_rate_limits",
column: "updated_at");
migrationBuilder.CreateIndex(
name: "ix_sms_verification_codes_tenant_id_phone_purpose",
table: "sms_verification_codes",
columns: new[] { "tenant_id", "phone", "purpose" },
unique: true,
filter: "consumed_at is null and status in ('pending', 'sent')");
migrationBuilder.CreateIndex(
name: "ix_sms_verification_codes_tenant_id_phone_purpose_created_at",
table: "sms_verification_codes",
columns: new[] { "tenant_id", "phone", "purpose", "created_at" },
filter: "consumed_at is null and status in ('pending', 'sent')");
migrationBuilder.CreateIndex(
name: "ix_sms_verification_codes_tenant_id_phone_purpose_expires_at",
table: "sms_verification_codes",
columns: new[] { "tenant_id", "phone", "purpose", "expires_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_auth_providers_tenant_id_provider",
table: "tenant_auth_providers",
columns: new[] { "tenant_id", "provider" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_auth_providers_tenant_id_provider_status",
table: "tenant_auth_providers",
columns: new[] { "tenant_id", "provider", "status" });
migrationBuilder.CreateIndex(
name: "ix_tenant_class_members_tenant_id_class_id_status_member_type",
table: "tenant_class_members",
columns: new[] { "tenant_id", "class_id", "status", "member_type" });
migrationBuilder.CreateIndex(
name: "ix_tenant_class_members_tenant_id_class_id_user_id_member_type",
table: "tenant_class_members",
columns: new[] { "tenant_id", "class_id", "user_id", "member_type" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_class_members_tenant_id_user_id_status_member_type",
table: "tenant_class_members",
columns: new[] { "tenant_id", "user_id", "status", "member_type" });
migrationBuilder.CreateIndex(
name: "ix_tenant_class_members_user_id",
table: "tenant_class_members",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_tenant_classes_created_by",
table: "tenant_classes",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_classes_tenant_id_code",
table: "tenant_classes",
columns: new[] { "tenant_id", "code" },
unique: true,
filter: "code is not null and code <> ''");
migrationBuilder.CreateIndex(
name: "ix_tenant_classes_tenant_id_legacy_id",
table: "tenant_classes",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_classes_tenant_id_region_id",
table: "tenant_classes",
columns: new[] { "tenant_id", "region_id" },
filter: "region_id is not null");
migrationBuilder.CreateIndex(
name: "ix_tenant_classes_tenant_id_status_sort_order_created_at",
table: "tenant_classes",
columns: new[] { "tenant_id", "status", "sort_order", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_classes_updated_by",
table: "tenant_classes",
column: "updated_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_role_templates_created_by",
table: "tenant_role_templates",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_role_templates_tenant_id_code",
table: "tenant_role_templates",
columns: new[] { "tenant_id", "code" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_role_templates_tenant_id_status_sort_order",
table: "tenant_role_templates",
columns: new[] { "tenant_id", "status", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_tenant_role_templates_updated_by",
table: "tenant_role_templates",
column: "updated_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_followups_assigned_to_user_id",
table: "tenant_student_followups",
column: "assigned_to_user_id");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_followups_completed_by",
table: "tenant_student_followups",
column: "completed_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_followups_student_user_id",
table: "tenant_student_followups",
column: "student_user_id");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_followups_tenant_id_assigned_to_user_id_stat~",
table: "tenant_student_followups",
columns: new[] { "tenant_id", "assigned_to_user_id", "status", "due_at" },
filter: "assigned_to_user_id is not null");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_followups_tenant_id_class_id_status_due_at",
table: "tenant_student_followups",
columns: new[] { "tenant_id", "class_id", "status", "due_at" },
filter: "class_id is not null");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_followups_tenant_id_student_user_id_status_d~",
table: "tenant_student_followups",
columns: new[] { "tenant_id", "student_user_id", "status", "due_at", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_student_notes_created_by",
table: "tenant_student_notes",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_notes_student_user_id",
table: "tenant_student_notes",
column: "student_user_id");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_notes_tenant_id_created_by_created_at",
table: "tenant_student_notes",
columns: new[] { "tenant_id", "created_by", "created_at" },
filter: "created_by is not null");
migrationBuilder.CreateIndex(
name: "ix_tenant_student_notes_tenant_id_student_user_id_is_pinned_cr~",
table: "tenant_student_notes",
columns: new[] { "tenant_id", "student_user_id", "is_pinned", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_student_notes_updated_by",
table: "tenant_student_notes",
column: "updated_by");
migrationBuilder.AddForeignKey(
name: "fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~",
table: "tenant_memberships",
columns: new[] { "tenant_id", "role_template_id" },
principalTable: "tenant_role_templates",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_tenant_memberships_tenant_role_templates_tenant_id_role_tem~",
table: "tenant_memberships");
migrationBuilder.DropTable(
name: "auth_login_events");
migrationBuilder.DropTable(
name: "auth_sessions");
migrationBuilder.DropTable(
name: "sms_send_rate_limits");
migrationBuilder.DropTable(
name: "sms_verification_codes");
migrationBuilder.DropTable(
name: "tenant_auth_providers");
migrationBuilder.DropTable(
name: "tenant_class_members");
migrationBuilder.DropTable(
name: "tenant_role_templates");
migrationBuilder.DropTable(
name: "tenant_student_followups");
migrationBuilder.DropTable(
name: "tenant_student_notes");
migrationBuilder.DropTable(
name: "tenant_classes");
migrationBuilder.DropIndex(
name: "ix_tenant_memberships_tenant_id_role_template_id",
table: "tenant_memberships");
migrationBuilder.DropColumn(
name: "role_template_id",
table: "tenant_memberships");
}
}
}

View File

@@ -1,853 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddCommercePersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "code_batches",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
sale_type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
channel = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
campaign_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
default_unit_price_cents = table.Column<int>(type: "integer", nullable: false),
cost_price_cents = table.Column<int>(type: "integer", nullable: false),
total_count = table.Column<int>(type: "integer", nullable: false),
days = table.Column<int>(type: "integer", nullable: true),
legacy_region_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
issued_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
remark = table.Column<string>(type: "text", nullable: true),
commission_rate = table.Column<decimal>(type: "numeric(6,4)", precision: 6, scale: 4, nullable: true),
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_code_batches", x => x.id);
table.UniqueConstraint("ak_code_batches_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_code_batches_amounts", "default_unit_price_cents >= 0 and cost_price_cents >= 0");
table.CheckConstraint("ck_code_batches_counts", "total_count >= 0");
table.ForeignKey(
name: "fk_code_batches_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "entitlements",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
entitlement_type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
scope_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
scope_id = table.Column<Guid>(type: "uuid", nullable: true),
source_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
source_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_source_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
starts_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_entitlements", x => x.id);
table.UniqueConstraint("ak_entitlements_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_entitlements_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_entitlements_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "products",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
legacy_price_text = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
link = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
tags = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
sort_order = table.Column<int>(type: "integer", nullable: false),
cover = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
preview_iframe = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
detail_images = 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_products", x => x.id);
table.UniqueConstraint("ak_products_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_products_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_products_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "svip_plans",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
price_cents = table.Column<int>(type: "integer", nullable: false),
original_price_cents = table.Column<int>(type: "integer", nullable: true),
days = table.Column<int>(type: "integer", nullable: false),
description = table.Column<string>(type: "text", nullable: true),
per_day_label = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
badge = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
recommended = table.Column<bool>(type: "boolean", nullable: false),
coupon_only = table.Column<bool>(type: "boolean", nullable: false),
vp_product_id = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
vp_enabled = table.Column<bool>(type: "boolean", nullable: false),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
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_svip_plans", x => x.id);
table.UniqueConstraint("ak_svip_plans_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_svip_plans_days", "days >= 0");
table.CheckConstraint("ck_svip_plans_price", "price_cents >= 0 and (original_price_cents is null or original_price_cents >= 0)");
table.ForeignKey(
name: "fk_svip_plans_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_svip_plans_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_payment_accounts",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
display_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
config_public = 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_tenant_payment_accounts", x => x.id);
table.UniqueConstraint("ak_tenant_payment_accounts_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_payment_accounts_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_subscriptions",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
plan_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
starts_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
billing_cycle = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
amount_cents = table.Column<int>(type: "integer", nullable: false),
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_tenant_subscriptions", x => x.id);
table.UniqueConstraint("ak_tenant_subscriptions_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_subscriptions_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_usage_records",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
metric_key = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
metric_value = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: false),
period_start = table.Column<DateOnly>(type: "date", nullable: false),
period_end = table.Column<DateOnly>(type: "date", nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_tenant_usage_records", x => x.id);
table.UniqueConstraint("ak_tenant_usage_records_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_usage_records_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "coupons",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
plan_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
code = table.Column<string>(type: "citext", maxLength: 100, nullable: false),
discount_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
discount_value = table.Column<decimal>(type: "numeric(10,2)", precision: 10, scale: 2, nullable: true),
valid_from = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
valid_to = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
max_uses = table.Column<int>(type: "integer", nullable: true),
used_count = table.Column<int>(type: "integer", nullable: false),
source = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
remark = table.Column<string>(type: "text", nullable: true),
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_coupons", x => x.id);
table.UniqueConstraint("ak_coupons_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_coupons_svip_plans_tenant_id_plan_id",
columns: x => new { x.tenant_id, x.plan_id },
principalTable: "svip_plans",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_coupons_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "orders",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: true),
plan_id = table.Column<Guid>(type: "uuid", nullable: true),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_user_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_plan_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_region_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
order_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
product_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
product_name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
amount_cents = table.Column<int>(type: "integer", nullable: false),
pay_method = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
pay_provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
trade_no = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
days = table.Column<int>(type: "integer", nullable: true),
paid_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
raw_payload = 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_orders", x => x.id);
table.UniqueConstraint("ak_orders_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_orders_amount", "amount_cents >= 0");
table.ForeignKey(
name: "fk_orders_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_orders_svip_plans_tenant_id_plan_id",
columns: x => new { x.tenant_id, x.plan_id },
principalTable: "svip_plans",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_orders_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_orders_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "coupon_redemptions",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
coupon_id = table.Column<Guid>(type: "uuid", nullable: true),
user_id = table.Column<Guid>(type: "uuid", nullable: true),
plan_id = table.Column<Guid>(type: "uuid", nullable: true),
order_id = table.Column<Guid>(type: "uuid", nullable: true),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
coupon_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
discount_applied_cents = table.Column<int>(type: "integer", nullable: true),
source = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
remark = table.Column<string>(type: "text", nullable: true),
claimed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
used_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_coupon_redemptions", x => x.id);
table.UniqueConstraint("ak_coupon_redemptions_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_coupon_redemptions_coupons_tenant_id_coupon_id",
columns: x => new { x.tenant_id, x.coupon_id },
principalTable: "coupons",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_coupon_redemptions_orders_tenant_id_order_id",
columns: x => new { x.tenant_id, x.order_id },
principalTable: "orders",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_coupon_redemptions_regions_tenant_id_region_id",
columns: x => new { x.tenant_id, x.region_id },
principalTable: "regions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_coupon_redemptions_svip_plans_tenant_id_plan_id",
columns: x => new { x.tenant_id, x.plan_id },
principalTable: "svip_plans",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_coupon_redemptions_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_coupon_redemptions_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "order_items",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
order_id = table.Column<Guid>(type: "uuid", nullable: false),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
item_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
item_id = table.Column<Guid>(type: "uuid", nullable: true),
name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
quantity = table.Column<int>(type: "integer", nullable: false),
unit_amount_cents = table.Column<int>(type: "integer", nullable: false),
total_amount_cents = table.Column<int>(type: "integer", nullable: false),
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_order_items", x => x.id);
table.UniqueConstraint("ak_order_items_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_order_items_amounts", "unit_amount_cents >= 0 and total_amount_cents >= 0");
table.CheckConstraint("ck_order_items_quantity", "quantity > 0");
table.ForeignKey(
name: "fk_order_items_orders_tenant_id_order_id",
columns: x => new { x.tenant_id, x.order_id },
principalTable: "orders",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_order_items_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "payments",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
order_id = table.Column<Guid>(type: "uuid", nullable: false),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
legacy_order_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
method = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
amount_cents = table.Column<int>(type: "integer", nullable: false),
provider_trade_no = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
paid_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
raw_payload = 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_payments", x => x.id);
table.UniqueConstraint("ak_payments_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_payments_amount", "amount_cents >= 0");
table.ForeignKey(
name: "fk_payments_orders_tenant_id_order_id",
columns: x => new { x.tenant_id, x.order_id },
principalTable: "orders",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_payments_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "activation_codes",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
used_by = table.Column<Guid>(type: "uuid", nullable: true),
agent_user_id = table.Column<Guid>(type: "uuid", nullable: true),
batch_id = table.Column<Guid>(type: "uuid", nullable: true),
used_region_id = table.Column<Guid>(type: "uuid", nullable: true),
coupon_redemption_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
code = table.Column<string>(type: "citext", maxLength: 100, nullable: false),
days = table.Column<int>(type: "integer", nullable: false),
is_used = table.Column<bool>(type: "boolean", nullable: false),
used_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
sale_type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
unit_price_cents = table.Column<int>(type: "integer", nullable: true),
sold_to = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
coupon_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
remark = table.Column<string>(type: "text", nullable: true),
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_activation_codes", x => x.id);
table.UniqueConstraint("ak_activation_codes_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_activation_codes_code_batches_tenant_id_batch_id",
columns: x => new { x.tenant_id, x.batch_id },
principalTable: "code_batches",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_activation_codes_coupon_redemptions_tenant_id_coupon_redemp~",
columns: x => new { x.tenant_id, x.coupon_redemption_id },
principalTable: "coupon_redemptions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_activation_codes_regions_tenant_id_used_region_id",
columns: x => new { x.tenant_id, x.used_region_id },
principalTable: "regions",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_activation_codes_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_activation_codes_users_agent_user_id",
column: x => x.agent_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_activation_codes_users_used_by",
column: x => x.used_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "payment_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
payment_id = table.Column<Guid>(type: "uuid", nullable: true),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
event_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
event_id = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
signature_valid = table.Column<bool>(type: "boolean", nullable: true),
payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
processed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
error = table.Column<string>(type: "text", nullable: true),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_payment_events", x => x.id);
table.UniqueConstraint("ak_payment_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_payment_events_payments_tenant_id_payment_id",
columns: x => new { x.tenant_id, x.payment_id },
principalTable: "payments",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_payment_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_activation_codes_agent_user_id",
table: "activation_codes",
column: "agent_user_id");
migrationBuilder.CreateIndex(
name: "ix_activation_codes_tenant_id_batch_id",
table: "activation_codes",
columns: new[] { "tenant_id", "batch_id" });
migrationBuilder.CreateIndex(
name: "ix_activation_codes_tenant_id_code",
table: "activation_codes",
columns: new[] { "tenant_id", "code" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_activation_codes_tenant_id_coupon_redemption_id",
table: "activation_codes",
columns: new[] { "tenant_id", "coupon_redemption_id" });
migrationBuilder.CreateIndex(
name: "ix_activation_codes_tenant_id_legacy_id",
table: "activation_codes",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_activation_codes_tenant_id_used_region_id",
table: "activation_codes",
columns: new[] { "tenant_id", "used_region_id" });
migrationBuilder.CreateIndex(
name: "ix_activation_codes_used_by",
table: "activation_codes",
column: "used_by");
migrationBuilder.CreateIndex(
name: "ix_code_batches_tenant_id_legacy_id",
table: "code_batches",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_coupon_redemptions_tenant_id_coupon_id",
table: "coupon_redemptions",
columns: new[] { "tenant_id", "coupon_id" });
migrationBuilder.CreateIndex(
name: "ix_coupon_redemptions_tenant_id_legacy_id",
table: "coupon_redemptions",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_coupon_redemptions_tenant_id_order_id_user_id",
table: "coupon_redemptions",
columns: new[] { "tenant_id", "order_id", "user_id" },
filter: "order_id is not null");
migrationBuilder.CreateIndex(
name: "ix_coupon_redemptions_tenant_id_plan_id",
table: "coupon_redemptions",
columns: new[] { "tenant_id", "plan_id" });
migrationBuilder.CreateIndex(
name: "ix_coupon_redemptions_tenant_id_region_id",
table: "coupon_redemptions",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_coupon_redemptions_tenant_id_user_id_coupon_id",
table: "coupon_redemptions",
columns: new[] { "tenant_id", "user_id", "coupon_id" },
unique: true,
filter: "coupon_id is not null");
migrationBuilder.CreateIndex(
name: "ix_coupon_redemptions_user_id",
table: "coupon_redemptions",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_coupons_tenant_id_code",
table: "coupons",
columns: new[] { "tenant_id", "code" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_coupons_tenant_id_legacy_id",
table: "coupons",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_coupons_tenant_id_plan_id",
table: "coupons",
columns: new[] { "tenant_id", "plan_id" });
migrationBuilder.CreateIndex(
name: "ix_entitlements_tenant_id_user_id_status_expires_at",
table: "entitlements",
columns: new[] { "tenant_id", "user_id", "status", "expires_at" });
migrationBuilder.CreateIndex(
name: "ix_entitlements_user_id",
table: "entitlements",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_order_items_tenant_id_legacy_id",
table: "order_items",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_order_items_tenant_id_order_id",
table: "order_items",
columns: new[] { "tenant_id", "order_id" });
migrationBuilder.CreateIndex(
name: "ix_orders_tenant_id_legacy_id",
table: "orders",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_orders_tenant_id_order_no",
table: "orders",
columns: new[] { "tenant_id", "order_no" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_orders_tenant_id_plan_id",
table: "orders",
columns: new[] { "tenant_id", "plan_id" });
migrationBuilder.CreateIndex(
name: "ix_orders_tenant_id_region_id",
table: "orders",
columns: new[] { "tenant_id", "region_id" });
migrationBuilder.CreateIndex(
name: "ix_orders_tenant_id_user_id_created_at",
table: "orders",
columns: new[] { "tenant_id", "user_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_orders_user_id",
table: "orders",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_payment_events_provider_event_id",
table: "payment_events",
columns: new[] { "provider", "event_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_payment_events_tenant_id_payment_id",
table: "payment_events",
columns: new[] { "tenant_id", "payment_id" });
migrationBuilder.CreateIndex(
name: "ix_payments_provider_provider_trade_no",
table: "payments",
columns: new[] { "provider", "provider_trade_no" },
unique: true,
filter: "provider_trade_no is not null");
migrationBuilder.CreateIndex(
name: "ix_payments_tenant_id_legacy_id",
table: "payments",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_payments_tenant_id_order_id_updated_at",
table: "payments",
columns: new[] { "tenant_id", "order_id", "updated_at" });
migrationBuilder.CreateIndex(
name: "ix_products_tenant_id_legacy_id",
table: "products",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_products_tenant_id_region_id_type_sort_order",
table: "products",
columns: new[] { "tenant_id", "region_id", "type", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_svip_plans_tenant_id_legacy_id",
table: "svip_plans",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_svip_plans_tenant_id_region_id_is_active_sort_order",
table: "svip_plans",
columns: new[] { "tenant_id", "region_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_tenant_payment_accounts_tenant_id_provider",
table: "tenant_payment_accounts",
columns: new[] { "tenant_id", "provider" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_subscriptions_tenant_id_status_expires_at",
table: "tenant_subscriptions",
columns: new[] { "tenant_id", "status", "expires_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_usage_records_tenant_id_metric_key_period_start_peri~",
table: "tenant_usage_records",
columns: new[] { "tenant_id", "metric_key", "period_start", "period_end" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "activation_codes");
migrationBuilder.DropTable(
name: "entitlements");
migrationBuilder.DropTable(
name: "order_items");
migrationBuilder.DropTable(
name: "payment_events");
migrationBuilder.DropTable(
name: "products");
migrationBuilder.DropTable(
name: "tenant_payment_accounts");
migrationBuilder.DropTable(
name: "tenant_subscriptions");
migrationBuilder.DropTable(
name: "tenant_usage_records");
migrationBuilder.DropTable(
name: "code_batches");
migrationBuilder.DropTable(
name: "coupon_redemptions");
migrationBuilder.DropTable(
name: "payments");
migrationBuilder.DropTable(
name: "coupons");
migrationBuilder.DropTable(
name: "orders");
migrationBuilder.DropTable(
name: "svip_plans");
}
}
}

View File

@@ -1,799 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddReferralCrmAndCommissionPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "commission_settlements",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
referrer_user_id = table.Column<Guid>(type: "uuid", nullable: false),
generated_by = table.Column<Guid>(type: "uuid", nullable: true),
reviewed_by = table.Column<Guid>(type: "uuid", nullable: true),
paid_by = table.Column<Guid>(type: "uuid", nullable: true),
settlement_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
period_start = table.Column<DateOnly>(type: "date", nullable: false),
period_end = table.Column<DateOnly>(type: "date", nullable: false),
source_count = table.Column<int>(type: "integer", nullable: false),
paid_user_count = table.Column<int>(type: "integer", nullable: false),
gross_amount_cents = table.Column<int>(type: "integer", nullable: false),
commission_amount_cents = table.Column<int>(type: "integer", nullable: false),
default_rate = table.Column<decimal>(type: "numeric(6,4)", precision: 6, scale: 4, nullable: false),
effective_rate = table.Column<decimal>(type: "numeric(6,4)", precision: 6, scale: 4, nullable: true),
reviewed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
paid_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
payment_method = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
payment_account = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
remark = table.Column<string>(type: "text", nullable: true),
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_commission_settlements", x => x.id);
table.UniqueConstraint("ak_commission_settlements_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0");
table.CheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0");
table.CheckConstraint("ck_commission_settlements_period", "period_end >= period_start");
table.CheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))");
table.ForeignKey(
name: "fk_commission_settlements_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commission_settlements_users_generated_by",
column: x => x.generated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_commission_settlements_users_paid_by",
column: x => x.paid_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_commission_settlements_users_referrer_user_id",
column: x => x.referrer_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commission_settlements_users_reviewed_by",
column: x => x.reviewed_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "crm_config",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
enabled = table.Column<bool>(type: "boolean", nullable: false),
url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
secret_ref = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
form_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
exam_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
timeout_seconds = table.Column<int>(type: "integer", nullable: true),
delay_seconds = table.Column<int>(type: "integer", nullable: true),
assignment_mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
assignment_pool = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
assignment_cursor = table.Column<int>(type: "integer", nullable: false),
assignment_config = 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_crm_config", x => x.id);
table.UniqueConstraint("ak_crm_config_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_crm_config_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "crm_webhook_log",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
record_id = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
lead_id = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
http_code = table.Column<int>(type: "integer", nullable: true),
outcome = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
error_message = table.Column<string>(type: "text", nullable: true),
request_body = table.Column<string>(type: "text", nullable: true),
request_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
response_summary = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
signed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
attempt = table.Column<int>(type: "integer", nullable: true),
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_crm_webhook_log", x => x.id);
table.UniqueConstraint("ak_crm_webhook_log_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_crm_webhook_log_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "crm_webhook_queue",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
record_id = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
lead_id = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
source = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
provider = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
scheduled_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
attempts = table.Column<int>(type: "integer", nullable: false),
next_attempt_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
last_attempt_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
last_error = table.Column<string>(type: "text", nullable: true),
last_http_code = table.Column<int>(type: "integer", nullable: true),
last_response_summary = table.Column<string>(type: "text", nullable: true),
sent_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
idempotency_key = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
target_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
payload = 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_crm_webhook_queue", x => x.id);
table.UniqueConstraint("ak_crm_webhook_queue_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0");
table.ForeignKey(
name: "fk_crm_webhook_queue_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "referral_codes",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
code = table.Column<string>(type: "citext", maxLength: 100, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
channel = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
landing_path = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
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_referral_codes", x => x.id);
table.UniqueConstraint("ak_referral_codes_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_referral_codes_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_referral_codes_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "referral_qrcodes",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: true),
ref_code = table.Column<string>(type: "citext", maxLength: 100, nullable: false),
scene = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
page = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
provider = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
qrcode_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
error_message = table.Column<string>(type: "text", nullable: true),
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_referral_qrcodes", x => x.id);
table.UniqueConstraint("ak_referral_qrcodes_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_referral_qrcodes_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_referral_qrcodes_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "referral_team_edges",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
member_user_id = table.Column<Guid>(type: "uuid", nullable: false),
leader_user_id = table.Column<Guid>(type: "uuid", nullable: true),
relation_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
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_referral_team_edges", x => x.id);
table.UniqueConstraint("ak_referral_team_edges_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_referral_team_edges_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_referral_team_edges_users_leader_user_id",
column: x => x.leader_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_referral_team_edges_users_member_user_id",
column: x => x.member_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_commission_settings",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
default_rate = table.Column<decimal>(type: "numeric(6,4)", precision: 6, scale: 4, nullable: false),
min_settlement_cents = table.Column<int>(type: "integer", nullable: false),
settlement_cycle = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
config = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_tenant_commission_settings", x => x.id);
table.UniqueConstraint("ak_tenant_commission_settings_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1");
table.CheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0");
table.ForeignKey(
name: "fk_tenant_commission_settings_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_commission_settings_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "commission_settlement_items",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
settlement_id = table.Column<Guid>(type: "uuid", nullable: true),
referrer_user_id = table.Column<Guid>(type: "uuid", nullable: false),
student_user_id = table.Column<Guid>(type: "uuid", nullable: true),
source_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
source_id = table.Column<Guid>(type: "uuid", nullable: false),
source_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
source_paid_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
gross_amount_cents = table.Column<int>(type: "integer", nullable: false),
commission_rate = table.Column<decimal>(type: "numeric(6,4)", precision: 6, scale: 4, nullable: false),
commission_amount_cents = table.Column<int>(type: "integer", nullable: false),
rate_source = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
attribution_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
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_commission_settlement_items", x => x.id);
table.UniqueConstraint("ak_commission_settlement_items_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0");
table.CheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1");
table.ForeignKey(
name: "fk_commission_settlement_items_commission_settlements_tenant_i~",
columns: x => new { x.tenant_id, x.settlement_id },
principalTable: "commission_settlements",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_commission_settlement_items_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commission_settlement_items_users_referrer_user_id",
column: x => x.referrer_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commission_settlement_items_users_student_user_id",
column: x => x.student_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "referral_leads",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
student_user_id = table.Column<Guid>(type: "uuid", nullable: false),
referrer_user_id = table.Column<Guid>(type: "uuid", nullable: true),
first_track_id = table.Column<Guid>(type: "uuid", nullable: true),
assigned_to_user_id = table.Column<Guid>(type: "uuid", nullable: true),
assigned_by = table.Column<Guid>(type: "uuid", nullable: true),
ref_code = table.Column<string>(type: "citext", maxLength: 100, nullable: true),
source = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
bind_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
protected_until = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
assignment_mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
assigned_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
bound_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
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_referral_leads", x => x.id);
table.UniqueConstraint("ak_referral_leads_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_referral_leads_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_referral_leads_users_assigned_by",
column: x => x.assigned_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_referral_leads_users_assigned_to_user_id",
column: x => x.assigned_to_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_referral_leads_users_referrer_user_id",
column: x => x.referrer_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_referral_leads_users_student_user_id",
column: x => x.student_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "referral_tracks",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
lead_id = table.Column<Guid>(type: "uuid", nullable: true),
referrer_user_id = table.Column<Guid>(type: "uuid", nullable: true),
target_user_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
event_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
ref_code = table.Column<string>(type: "citext", maxLength: 100, nullable: true),
source = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
ip_address = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
user_agent = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
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_referral_tracks", x => x.id);
table.UniqueConstraint("ak_referral_tracks_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_referral_tracks_referral_leads_tenant_id_lead_id",
columns: x => new { x.tenant_id, x.lead_id },
principalTable: "referral_leads",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_referral_tracks_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_referral_tracks_users_referrer_user_id",
column: x => x.referrer_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_referral_tracks_users_target_user_id",
column: x => x.target_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateIndex(
name: "ix_commission_settlement_items_referrer_user_id",
table: "commission_settlement_items",
column: "referrer_user_id");
migrationBuilder.CreateIndex(
name: "ix_commission_settlement_items_student_user_id",
table: "commission_settlement_items",
column: "student_user_id");
migrationBuilder.CreateIndex(
name: "ix_commission_settlement_items_tenant_id_referrer_user_id_sour~",
table: "commission_settlement_items",
columns: new[] { "tenant_id", "referrer_user_id", "source_paid_at" });
migrationBuilder.CreateIndex(
name: "ix_commission_settlement_items_tenant_id_settlement_id",
table: "commission_settlement_items",
columns: new[] { "tenant_id", "settlement_id" });
migrationBuilder.CreateIndex(
name: "ix_commission_settlement_items_tenant_id_source_type_source_id",
table: "commission_settlement_items",
columns: new[] { "tenant_id", "source_type", "source_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_commission_settlements_generated_by",
table: "commission_settlements",
column: "generated_by");
migrationBuilder.CreateIndex(
name: "ix_commission_settlements_paid_by",
table: "commission_settlements",
column: "paid_by");
migrationBuilder.CreateIndex(
name: "ix_commission_settlements_referrer_start",
table: "commission_settlements",
columns: new[] { "tenant_id", "referrer_user_id", "period_start" });
migrationBuilder.CreateIndex(
name: "ix_commission_settlements_referrer_user_id",
table: "commission_settlements",
column: "referrer_user_id");
migrationBuilder.CreateIndex(
name: "ix_commission_settlements_reviewed_by",
table: "commission_settlements",
column: "reviewed_by");
migrationBuilder.CreateIndex(
name: "ix_commission_settlements_tenant_id_settlement_no",
table: "commission_settlements",
columns: new[] { "tenant_id", "settlement_no" },
unique: true);
migrationBuilder.CreateIndex(
name: "ux_commission_settlements_referrer_period",
table: "commission_settlements",
columns: new[] { "tenant_id", "referrer_user_id", "period_start", "period_end" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_crm_config_tenant_id",
table: "crm_config",
column: "tenant_id",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_crm_config_tenant_id_assignment_mode",
table: "crm_config",
columns: new[] { "tenant_id", "assignment_mode" });
migrationBuilder.CreateIndex(
name: "ix_crm_webhook_log_tenant_id_legacy_id",
table: "crm_webhook_log",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_crm_webhook_log_tenant_id_record_id_created_at",
table: "crm_webhook_log",
columns: new[] { "tenant_id", "record_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_crm_webhook_queue_tenant_id_idempotency_key",
table: "crm_webhook_queue",
columns: new[] { "tenant_id", "idempotency_key" },
unique: true,
filter: "idempotency_key is not null");
migrationBuilder.CreateIndex(
name: "ix_crm_webhook_queue_tenant_id_legacy_id",
table: "crm_webhook_queue",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_crm_webhook_queue_tenant_id_record_id",
table: "crm_webhook_queue",
columns: new[] { "tenant_id", "record_id" },
unique: true,
filter: "record_id is not null");
migrationBuilder.CreateIndex(
name: "ix_crm_webhook_queue_tenant_id_record_id_created_at",
table: "crm_webhook_queue",
columns: new[] { "tenant_id", "record_id", "created_at" },
filter: "record_id is not null");
migrationBuilder.CreateIndex(
name: "ix_crm_webhook_queue_tenant_id_source_created_at",
table: "crm_webhook_queue",
columns: new[] { "tenant_id", "source", "created_at" },
filter: "source = 'tenant.student.crm_push'");
migrationBuilder.CreateIndex(
name: "ix_crm_webhook_queue_tenant_id_status_scheduled_at_next_attemp~",
table: "crm_webhook_queue",
columns: new[] { "tenant_id", "status", "scheduled_at", "next_attempt_at" });
migrationBuilder.CreateIndex(
name: "ix_referral_codes_tenant_id_code",
table: "referral_codes",
columns: new[] { "tenant_id", "code" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_referral_codes_tenant_id_code_status",
table: "referral_codes",
columns: new[] { "tenant_id", "code", "status" });
migrationBuilder.CreateIndex(
name: "ix_referral_codes_tenant_id_user_id",
table: "referral_codes",
columns: new[] { "tenant_id", "user_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_referral_codes_user_id",
table: "referral_codes",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_leads_assigned_by",
table: "referral_leads",
column: "assigned_by");
migrationBuilder.CreateIndex(
name: "ix_referral_leads_assigned_to_user_id",
table: "referral_leads",
column: "assigned_to_user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_leads_referrer_user_id",
table: "referral_leads",
column: "referrer_user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_leads_student_user_id",
table: "referral_leads",
column: "student_user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_leads_tenant_id_assigned_to_user_id_status_bound_at",
table: "referral_leads",
columns: new[] { "tenant_id", "assigned_to_user_id", "status", "bound_at" });
migrationBuilder.CreateIndex(
name: "ix_referral_leads_tenant_id_first_track_id",
table: "referral_leads",
columns: new[] { "tenant_id", "first_track_id" });
migrationBuilder.CreateIndex(
name: "ix_referral_leads_tenant_id_referrer_user_id_bound_at",
table: "referral_leads",
columns: new[] { "tenant_id", "referrer_user_id", "bound_at" });
migrationBuilder.CreateIndex(
name: "ix_referral_leads_tenant_id_student_user_id",
table: "referral_leads",
columns: new[] { "tenant_id", "student_user_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_referral_qrcodes_tenant_id_provider_scene_page",
table: "referral_qrcodes",
columns: new[] { "tenant_id", "provider", "scene", "page" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_referral_qrcodes_tenant_id_ref_code_status",
table: "referral_qrcodes",
columns: new[] { "tenant_id", "ref_code", "status" });
migrationBuilder.CreateIndex(
name: "ix_referral_qrcodes_user_id",
table: "referral_qrcodes",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_team_edges_leader_user_id",
table: "referral_team_edges",
column: "leader_user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_team_edges_member_user_id",
table: "referral_team_edges",
column: "member_user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_team_edges_tenant_id_leader_user_id_status",
table: "referral_team_edges",
columns: new[] { "tenant_id", "leader_user_id", "status" });
migrationBuilder.CreateIndex(
name: "ix_referral_team_edges_tenant_id_member_user_id_relation_type",
table: "referral_team_edges",
columns: new[] { "tenant_id", "member_user_id", "relation_type" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_referral_tracks_referrer_user_id",
table: "referral_tracks",
column: "referrer_user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_tracks_target_user_id",
table: "referral_tracks",
column: "target_user_id");
migrationBuilder.CreateIndex(
name: "ix_referral_tracks_tenant_id_lead_id_created_at",
table: "referral_tracks",
columns: new[] { "tenant_id", "lead_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_referral_tracks_tenant_id_legacy_id",
table: "referral_tracks",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_referral_tracks_tenant_id_ref_code_event_type_created_at",
table: "referral_tracks",
columns: new[] { "tenant_id", "ref_code", "event_type", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_commission_settings_tenant_id",
table: "tenant_commission_settings",
column: "tenant_id",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_commission_settings_updated_by",
table: "tenant_commission_settings",
column: "updated_by");
migrationBuilder.AddForeignKey(
name: "fk_referral_leads_referral_tracks_tenant_id_first_track_id",
table: "referral_leads",
columns: new[] { "tenant_id", "first_track_id" },
principalTable: "referral_tracks",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.SetNull);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_referral_leads_referral_tracks_tenant_id_first_track_id",
table: "referral_leads");
migrationBuilder.DropTable(
name: "commission_settlement_items");
migrationBuilder.DropTable(
name: "crm_config");
migrationBuilder.DropTable(
name: "crm_webhook_log");
migrationBuilder.DropTable(
name: "crm_webhook_queue");
migrationBuilder.DropTable(
name: "referral_codes");
migrationBuilder.DropTable(
name: "referral_qrcodes");
migrationBuilder.DropTable(
name: "referral_team_edges");
migrationBuilder.DropTable(
name: "tenant_commission_settings");
migrationBuilder.DropTable(
name: "commission_settlements");
migrationBuilder.DropTable(
name: "referral_tracks");
migrationBuilder.DropTable(
name: "referral_leads");
}
}
}

View File

@@ -1,619 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddOperationsContentAndAuditPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "announcements",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
content = table.Column<string>(type: "text", nullable: true),
link = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
background_color = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
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_announcements", x => x.id);
table.UniqueConstraint("ak_announcements_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_announcements_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "audit_logs",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: true),
actor_user_id = table.Column<Guid>(type: "uuid", nullable: true),
action = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
target_type = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
target_id = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
details = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
ip_address = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
user_agent = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_audit_logs", x => x.id);
table.ForeignKey(
name: "fk_audit_logs_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_audit_logs_users_actor_user_id",
column: x => x.actor_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "badges",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
category = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
icon_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
level = table.Column<int>(type: "integer", nullable: true),
unlock_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
condition_field = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
condition_operator = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
condition_value = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: true),
condition_extra = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
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_badges", x => x.id);
table.UniqueConstraint("ak_badges_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_badges_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "banners",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
subtitle = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
content = table.Column<string>(type: "text", nullable: true),
button_text = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
button_link = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
background_color = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
border_color = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
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_banners", x => x.id);
table.UniqueConstraint("ak_banners_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_banners_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_banners_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "faqs",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
question = table.Column<string>(type: "text", nullable: true),
answer = table.Column<string>(type: "text", nullable: true),
sort_order = table.Column<int>(type: "integer", nullable: false),
is_active = table.Column<bool>(type: "boolean", nullable: false),
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_faqs", x => x.id);
table.UniqueConstraint("ak_faqs_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_faqs_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_faqs_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_content_notifications",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
adoption_id = table.Column<Guid>(type: "uuid", nullable: true),
source_question_bank_id = table.Column<Guid>(type: "uuid", nullable: true),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
read_by = table.Column<Guid>(type: "uuid", nullable: true),
notification_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
message = table.Column<string>(type: "text", nullable: false),
action_label = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
action_path = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
dedupe_key = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
read_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
resolved_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_tenant_content_notifications", x => x.id);
table.UniqueConstraint("ak_tenant_content_notifications_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_content_notifications_question_banks_tenant_id_sourc~",
columns: x => new { x.tenant_id, x.source_question_bank_id },
principalTable: "question_banks",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_content_notifications_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_content_notifications_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_content_notifications_users_read_by",
column: x => x.read_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "tenant_theme_templates",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
preview_image_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
theme = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
public_assets = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
sort_order = table.Column<int>(type: "integer", 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_tenant_theme_templates", x => x.id);
table.UniqueConstraint("ak_tenant_theme_templates_code", x => x.code);
});
migrationBuilder.CreateTable(
name: "user_notifications",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
source_id = table.Column<Guid>(type: "uuid", nullable: true),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
notification_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
message = table.Column<string>(type: "text", nullable: false),
action_label = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
action_path = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
source_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
dedupe_key = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
read_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_user_notifications", x => x.id);
table.UniqueConstraint("ak_user_notifications_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_user_notifications_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_user_notifications_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_user_notifications_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "user_badges",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: true),
badge_id = table.Column<Guid>(type: "uuid", nullable: true),
granted_by = table.Column<Guid>(type: "uuid", nullable: true),
legacy_id = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
note = table.Column<string>(type: "text", nullable: true),
granted_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_user_badges", x => x.id);
table.UniqueConstraint("ak_user_badges_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_user_badges_badges_tenant_id_badge_id",
columns: x => new { x.tenant_id, x.badge_id },
principalTable: "badges",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_user_badges_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_user_badges_users_granted_by",
column: x => x.granted_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_user_badges_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_theme_configs",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
active_template_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
active_theme = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
active_public_assets = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
draft_template_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
draft_theme = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
draft_public_assets = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
published_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
published_by = table.Column<Guid>(type: "uuid", nullable: true),
draft_updated_by = table.Column<Guid>(type: "uuid", nullable: true),
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_tenant_theme_configs", x => x.id);
table.UniqueConstraint("ak_tenant_theme_configs_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_theme_configs_tenant_theme_templates_active_template~",
column: x => x.active_template_code,
principalTable: "tenant_theme_templates",
principalColumn: "code",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_theme_configs_tenant_theme_templates_draft_template_~",
column: x => x.draft_template_code,
principalTable: "tenant_theme_templates",
principalColumn: "code",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_theme_configs_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_theme_configs_users_draft_updated_by",
column: x => x.draft_updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_theme_configs_users_published_by",
column: x => x.published_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateIndex(
name: "ix_announcements_tenant_id_is_active_sort_order",
table: "announcements",
columns: new[] { "tenant_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_announcements_tenant_id_legacy_id",
table: "announcements",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_audit_logs_actor_user_id",
table: "audit_logs",
column: "actor_user_id");
migrationBuilder.CreateIndex(
name: "ix_audit_logs_tenant_id_created_at",
table: "audit_logs",
columns: new[] { "tenant_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_audit_logs_tenant_id_target_type_target_id_created_at",
table: "audit_logs",
columns: new[] { "tenant_id", "target_type", "target_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_badges_tenant_id_category_is_active_sort_order",
table: "badges",
columns: new[] { "tenant_id", "category", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_badges_tenant_id_legacy_id",
table: "badges",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_banners_tenant_id_legacy_id",
table: "banners",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_banners_tenant_id_region_id_is_active_sort_order",
table: "banners",
columns: new[] { "tenant_id", "region_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_faqs_tenant_id_legacy_id",
table: "faqs",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_faqs_tenant_id_region_id_is_active_sort_order",
table: "faqs",
columns: new[] { "tenant_id", "region_id", "is_active", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_tenant_content_notifications_created_by",
table: "tenant_content_notifications",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_content_notifications_read_by",
table: "tenant_content_notifications",
column: "read_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_content_notifications_tenant_id_adoption_id_notifica~",
table: "tenant_content_notifications",
columns: new[] { "tenant_id", "adoption_id", "notification_type", "status" });
migrationBuilder.CreateIndex(
name: "ix_tenant_content_notifications_tenant_id_notification_type_de~",
table: "tenant_content_notifications",
columns: new[] { "tenant_id", "notification_type", "dedupe_key" },
unique: true,
filter: "dedupe_key is not null");
migrationBuilder.CreateIndex(
name: "ix_tenant_content_notifications_tenant_id_source_question_bank~",
table: "tenant_content_notifications",
columns: new[] { "tenant_id", "source_question_bank_id" });
migrationBuilder.CreateIndex(
name: "ix_tenant_content_notifications_tenant_id_status_created_at",
table: "tenant_content_notifications",
columns: new[] { "tenant_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_theme_configs_active_template_code",
table: "tenant_theme_configs",
column: "active_template_code");
migrationBuilder.CreateIndex(
name: "ix_tenant_theme_configs_draft_template_code",
table: "tenant_theme_configs",
column: "draft_template_code");
migrationBuilder.CreateIndex(
name: "ix_tenant_theme_configs_draft_updated_by",
table: "tenant_theme_configs",
column: "draft_updated_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_theme_configs_published_by",
table: "tenant_theme_configs",
column: "published_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_theme_configs_tenant_id",
table: "tenant_theme_configs",
column: "tenant_id",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_theme_templates_code",
table: "tenant_theme_templates",
column: "code",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_theme_templates_status_sort_order_code",
table: "tenant_theme_templates",
columns: new[] { "status", "sort_order", "code" });
migrationBuilder.CreateIndex(
name: "ix_user_badges_granted_by",
table: "user_badges",
column: "granted_by");
migrationBuilder.CreateIndex(
name: "ix_user_badges_tenant_id_badge_id",
table: "user_badges",
columns: new[] { "tenant_id", "badge_id" });
migrationBuilder.CreateIndex(
name: "ix_user_badges_tenant_id_legacy_id",
table: "user_badges",
columns: new[] { "tenant_id", "legacy_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_user_badges_tenant_id_user_id_badge_id",
table: "user_badges",
columns: new[] { "tenant_id", "user_id", "badge_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_user_badges_user_id",
table: "user_badges",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_user_notifications_created_by",
table: "user_notifications",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_user_notifications_tenant_id_source_type_source_id",
table: "user_notifications",
columns: new[] { "tenant_id", "source_type", "source_id" });
migrationBuilder.CreateIndex(
name: "ix_user_notifications_tenant_id_user_id_notification_type_dedu~",
table: "user_notifications",
columns: new[] { "tenant_id", "user_id", "notification_type", "dedupe_key" },
unique: true,
filter: "dedupe_key is not null");
migrationBuilder.CreateIndex(
name: "ix_user_notifications_tenant_id_user_id_status_created_at",
table: "user_notifications",
columns: new[] { "tenant_id", "user_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_user_notifications_user_id",
table: "user_notifications",
column: "user_id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "announcements");
migrationBuilder.DropTable(
name: "audit_logs");
migrationBuilder.DropTable(
name: "banners");
migrationBuilder.DropTable(
name: "faqs");
migrationBuilder.DropTable(
name: "tenant_content_notifications");
migrationBuilder.DropTable(
name: "tenant_theme_configs");
migrationBuilder.DropTable(
name: "user_badges");
migrationBuilder.DropTable(
name: "user_notifications");
migrationBuilder.DropTable(
name: "tenant_theme_templates");
migrationBuilder.DropTable(
name: "badges");
}
}
}

View File

@@ -1,669 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddCommerceRefundAndReconciliationPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "refunded_amount_cents",
table: "payments",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "refunded_amount_cents",
table: "orders",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<DateTimeOffset>(
name: "revoked_at",
table: "entitlements",
type: "timestamp with time zone",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "revoked_by",
table: "entitlements",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "revoked_reason",
table: "entitlements",
type: "character varying(1000)",
maxLength: 1000,
nullable: true);
migrationBuilder.CreateTable(
name: "commerce_reconciliation_batches",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
bill_date = table.Column<DateOnly>(type: "date", nullable: false),
bill_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
source = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
source_name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
source_hash = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
total_count = table.Column<int>(type: "integer", nullable: false),
matched_count = table.Column<int>(type: "integer", nullable: false),
mismatch_count = table.Column<int>(type: "integer", nullable: false),
missing_local_count = table.Column<int>(type: "integer", nullable: false),
missing_provider_count = table.Column<int>(type: "integer", nullable: false),
duplicate_count = table.Column<int>(type: "integer", nullable: false),
ignored_count = table.Column<int>(type: "integer", nullable: false),
amount_cents = table.Column<int>(type: "integer", nullable: false),
refund_amount_cents = table.Column<int>(type: "integer", nullable: false),
fee_cents = table.Column<int>(type: "integer", nullable: false),
completed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
error = table.Column<string>(type: "text", nullable: true),
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_commerce_reconciliation_batches", x => x.id);
table.UniqueConstraint("ak_commerce_reconciliation_batches_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_commerce_reconciliation_batches_amounts", "amount_cents >= 0 and refund_amount_cents >= 0");
table.CheckConstraint("ck_commerce_reconciliation_batches_counts", "total_count >= 0 and matched_count >= 0 and mismatch_count >= 0 and missing_local_count >= 0 and missing_provider_count >= 0 and duplicate_count >= 0 and ignored_count >= 0");
table.ForeignKey(
name: "fk_commerce_reconciliation_batches_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_reconciliation_batches_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "commerce_refund_requests",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
order_id = table.Column<Guid>(type: "uuid", nullable: false),
payment_id = table.Column<Guid>(type: "uuid", nullable: true),
requested_by = table.Column<Guid>(type: "uuid", nullable: true),
reviewed_by = table.Column<Guid>(type: "uuid", nullable: true),
processed_by = table.Column<Guid>(type: "uuid", nullable: true),
refund_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
provider_refund_no = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
amount_cents = table.Column<int>(type: "integer", nullable: false),
reason = table.Column<string>(type: "text", nullable: true),
entitlement_action = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
requested_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
reviewed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
processed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
succeeded_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
failed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
cancelled_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
failure_reason = table.Column<string>(type: "text", nullable: true),
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_commerce_refund_requests", x => x.id);
table.UniqueConstraint("ak_commerce_refund_requests_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_commerce_refund_requests_amount", "amount_cents > 0");
table.ForeignKey(
name: "fk_commerce_refund_requests_orders_tenant_id_order_id",
columns: x => new { x.tenant_id, x.order_id },
principalTable: "orders",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_refund_requests_payments_tenant_id_payment_id",
columns: x => new { x.tenant_id, x.payment_id },
principalTable: "payments",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_refund_requests_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_refund_requests_users_processed_by",
column: x => x.processed_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_commerce_refund_requests_users_requested_by",
column: x => x.requested_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_commerce_refund_requests_users_reviewed_by",
column: x => x.reviewed_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "commerce_reconciliation_items",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
batch_id = table.Column<Guid>(type: "uuid", nullable: false),
order_id = table.Column<Guid>(type: "uuid", nullable: true),
payment_id = table.Column<Guid>(type: "uuid", nullable: true),
refund_request_id = table.Column<Guid>(type: "uuid", nullable: true),
row_no = table.Column<int>(type: "integer", nullable: false),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
transaction_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
provider_trade_no = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
provider_refund_no = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
order_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
refund_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
amount_cents = table.Column<int>(type: "integer", nullable: false),
refund_amount_cents = table.Column<int>(type: "integer", nullable: false),
fee_cents = table.Column<int>(type: "integer", nullable: false),
paid_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
refunded_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
provider_status = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
local_status = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
match_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
issue_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
details = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_commerce_reconciliation_items", x => x.id);
table.UniqueConstraint("ak_commerce_reconciliation_items_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_commerce_reconciliation_items_amounts", "amount_cents >= 0 and refund_amount_cents >= 0");
table.CheckConstraint("ck_commerce_reconciliation_items_row", "row_no > 0");
table.ForeignKey(
name: "fk_commerce_reconciliation_items_commerce_reconciliation_batch~",
columns: x => new { x.tenant_id, x.batch_id },
principalTable: "commerce_reconciliation_batches",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_reconciliation_items_commerce_refund_requests_tena~",
columns: x => new { x.tenant_id, x.refund_request_id },
principalTable: "commerce_refund_requests",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_reconciliation_items_orders_tenant_id_order_id",
columns: x => new { x.tenant_id, x.order_id },
principalTable: "orders",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_reconciliation_items_payments_tenant_id_payment_id",
columns: x => new { x.tenant_id, x.payment_id },
principalTable: "payments",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_reconciliation_items_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "commerce_refund_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
refund_request_id = table.Column<Guid>(type: "uuid", nullable: false),
from_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
to_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
event_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
actor_user_id = table.Column<Guid>(type: "uuid", nullable: true),
details = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_commerce_refund_events", x => x.id);
table.UniqueConstraint("ak_commerce_refund_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_commerce_refund_events_commerce_refund_requests_tenant_id_r~",
columns: x => new { x.tenant_id, x.refund_request_id },
principalTable: "commerce_refund_requests",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_refund_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_refund_events_users_actor_user_id",
column: x => x.actor_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "commerce_reconciliation_issues",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
batch_id = table.Column<Guid>(type: "uuid", nullable: true),
item_id = table.Column<Guid>(type: "uuid", nullable: true),
order_id = table.Column<Guid>(type: "uuid", nullable: true),
payment_id = table.Column<Guid>(type: "uuid", nullable: true),
refund_request_id = table.Column<Guid>(type: "uuid", nullable: true),
assigned_to = table.Column<Guid>(type: "uuid", nullable: true),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
resolved_by = table.Column<Guid>(type: "uuid", nullable: true),
issue_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
transaction_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
issue_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
match_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
resolution_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
order_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
refund_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
provider_trade_no = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
provider_refund_no = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
amount_cents = table.Column<int>(type: "integer", nullable: false),
refund_amount_cents = table.Column<int>(type: "integer", nullable: false),
resolved_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
due_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
summary = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
resolution_note = table.Column<string>(type: "text", nullable: true),
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_commerce_reconciliation_issues", x => x.id);
table.UniqueConstraint("ak_commerce_reconciliation_issues_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_commerce_reconciliation_issues_amounts", "amount_cents >= 0 and refund_amount_cents >= 0");
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_commerce_reconciliation_batc~",
columns: x => new { x.tenant_id, x.batch_id },
principalTable: "commerce_reconciliation_batches",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_commerce_reconciliation_item~",
columns: x => new { x.tenant_id, x.item_id },
principalTable: "commerce_reconciliation_items",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_commerce_refund_requests_ten~",
columns: x => new { x.tenant_id, x.refund_request_id },
principalTable: "commerce_refund_requests",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_orders_tenant_id_order_id",
columns: x => new { x.tenant_id, x.order_id },
principalTable: "orders",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_payments_tenant_id_payment_id",
columns: x => new { x.tenant_id, x.payment_id },
principalTable: "payments",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_users_assigned_to",
column: x => x.assigned_to,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_commerce_reconciliation_issues_users_resolved_by",
column: x => x.resolved_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "commerce_reconciliation_issue_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
issue_id = table.Column<Guid>(type: "uuid", nullable: false),
from_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
to_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
event_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
actor_user_id = table.Column<Guid>(type: "uuid", nullable: true),
note = table.Column<string>(type: "text", nullable: true),
details = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_commerce_reconciliation_issue_events", x => x.id);
table.UniqueConstraint("ak_commerce_reconciliation_issue_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_commerce_reconciliation_issue_events_commerce_reconciliatio~",
columns: x => new { x.tenant_id, x.issue_id },
principalTable: "commerce_reconciliation_issues",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_reconciliation_issue_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_commerce_reconciliation_issue_events_users_actor_user_id",
column: x => x.actor_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.AddCheckConstraint(
name: "ck_payments_refunded_amount",
table: "payments",
sql: "refunded_amount_cents >= 0");
migrationBuilder.AddCheckConstraint(
name: "ck_orders_refunded_amount",
table: "orders",
sql: "refunded_amount_cents >= 0");
migrationBuilder.CreateIndex(
name: "ix_entitlements_revoked_by",
table: "entitlements",
column: "revoked_by");
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_batches_created_by",
table: "commerce_reconciliation_batches",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_batches_tenant_id_provider_bill_dat~",
table: "commerce_reconciliation_batches",
columns: new[] { "tenant_id", "provider", "bill_date", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_batches_tenant_id_status_created_at",
table: "commerce_reconciliation_batches",
columns: new[] { "tenant_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issue_events_actor_user_id",
table: "commerce_reconciliation_issue_events",
column: "actor_user_id");
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issue_events_tenant_id_issue_id_cre~",
table: "commerce_reconciliation_issue_events",
columns: new[] { "tenant_id", "issue_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_assigned_to",
table: "commerce_reconciliation_issues",
column: "assigned_to");
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_created_by",
table: "commerce_reconciliation_issues",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_resolved_by",
table: "commerce_reconciliation_issues",
column: "resolved_by");
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_assigned_to_status~",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "assigned_to", "status", "due_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_batch_id_status_cr~",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "batch_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_issue_no",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "issue_no" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_item_id",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "item_id" },
unique: true,
filter: "item_id is not null and status in ('open', 'investigating', 'escalated')");
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_order_id",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "order_id" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_order_no_created_at",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "order_no", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_payment_id_created~",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "payment_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_refund_request_id",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "refund_request_id" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_issues_tenant_id_status_severity_cr~",
table: "commerce_reconciliation_issues",
columns: new[] { "tenant_id", "status", "severity", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_items_batch_id_row_no",
table: "commerce_reconciliation_items",
columns: new[] { "batch_id", "row_no" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_items_tenant_id_batch_id_match_stat~",
table: "commerce_reconciliation_items",
columns: new[] { "tenant_id", "batch_id", "match_status", "row_no" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_items_tenant_id_order_id",
table: "commerce_reconciliation_items",
columns: new[] { "tenant_id", "order_id" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_items_tenant_id_order_no_created_at",
table: "commerce_reconciliation_items",
columns: new[] { "tenant_id", "order_no", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_items_tenant_id_payment_id",
table: "commerce_reconciliation_items",
columns: new[] { "tenant_id", "payment_id" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_items_tenant_id_provider_provider_t~",
table: "commerce_reconciliation_items",
columns: new[] { "tenant_id", "provider", "provider_trade_no", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_items_tenant_id_refund_no_provider_~",
table: "commerce_reconciliation_items",
columns: new[] { "tenant_id", "refund_no", "provider_refund_no", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_reconciliation_items_tenant_id_refund_request_id",
table: "commerce_reconciliation_items",
columns: new[] { "tenant_id", "refund_request_id" });
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_events_actor_user_id",
table: "commerce_refund_events",
column: "actor_user_id");
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_events_tenant_id_refund_request_id_created_~",
table: "commerce_refund_events",
columns: new[] { "tenant_id", "refund_request_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_requests_processed_by",
table: "commerce_refund_requests",
column: "processed_by");
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_requests_requested_by",
table: "commerce_refund_requests",
column: "requested_by");
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_requests_reviewed_by",
table: "commerce_refund_requests",
column: "reviewed_by");
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_requests_tenant_id_order_id_created_at",
table: "commerce_refund_requests",
columns: new[] { "tenant_id", "order_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_requests_tenant_id_payment_id",
table: "commerce_refund_requests",
columns: new[] { "tenant_id", "payment_id" });
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_requests_tenant_id_refund_no",
table: "commerce_refund_requests",
columns: new[] { "tenant_id", "refund_no" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_commerce_refund_requests_tenant_id_status_created_at",
table: "commerce_refund_requests",
columns: new[] { "tenant_id", "status", "created_at" });
migrationBuilder.AddForeignKey(
name: "fk_entitlements_users_revoked_by",
table: "entitlements",
column: "revoked_by",
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_entitlements_users_revoked_by",
table: "entitlements");
migrationBuilder.DropTable(
name: "commerce_reconciliation_issue_events");
migrationBuilder.DropTable(
name: "commerce_refund_events");
migrationBuilder.DropTable(
name: "commerce_reconciliation_issues");
migrationBuilder.DropTable(
name: "commerce_reconciliation_items");
migrationBuilder.DropTable(
name: "commerce_reconciliation_batches");
migrationBuilder.DropTable(
name: "commerce_refund_requests");
migrationBuilder.DropCheckConstraint(
name: "ck_payments_refunded_amount",
table: "payments");
migrationBuilder.DropCheckConstraint(
name: "ck_orders_refunded_amount",
table: "orders");
migrationBuilder.DropIndex(
name: "ix_entitlements_revoked_by",
table: "entitlements");
migrationBuilder.DropColumn(
name: "refunded_amount_cents",
table: "payments");
migrationBuilder.DropColumn(
name: "refunded_amount_cents",
table: "orders");
migrationBuilder.DropColumn(
name: "revoked_at",
table: "entitlements");
migrationBuilder.DropColumn(
name: "revoked_by",
table: "entitlements");
migrationBuilder.DropColumn(
name: "revoked_reason",
table: "entitlements");
}
}
}

View File

@@ -1,452 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddContentPlatformOperationsPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "security_scan_provider",
table: "content_assets",
type: "character varying(100)",
maxLength: 100,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "security_scan_status",
table: "content_assets",
type: "character varying(32)",
maxLength: 32,
nullable: false,
defaultValue: "not_required");
migrationBuilder.AddColumn<JsonElement>(
name: "security_scan_summary",
table: "content_assets",
type: "jsonb",
nullable: false,
defaultValueSql: "'{}'::jsonb");
migrationBuilder.AddColumn<DateTimeOffset>(
name: "security_scanned_at",
table: "content_assets",
type: "timestamp with time zone",
nullable: true);
migrationBuilder.CreateTable(
name: "ai_recommendation_reports",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
region_id = table.Column<Guid>(type: "uuid", nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
provider = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
model = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
prompt_version = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
input_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
context_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
result_payload = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
error_message = table.Column<string>(type: "text", nullable: true),
generated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_ai_recommendation_reports", x => x.id);
table.UniqueConstraint("ak_ai_recommendation_reports_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_ai_recommendation_reports_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_ai_recommendation_reports_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_ai_recommendation_reports_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "content_asset_access_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
asset_id = table.Column<Guid>(type: "uuid", nullable: true),
user_id = table.Column<Guid>(type: "uuid", nullable: true),
actor_role = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
access_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
visibility = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
asset_type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
storage_provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
disposition = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
expires_in_seconds = table.Column<int>(type: "integer", nullable: true),
signature_mode = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
result = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
deny_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
ip_address = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
user_agent = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_content_asset_access_events", x => x.id);
table.UniqueConstraint("ak_content_asset_access_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0");
table.ForeignKey(
name: "fk_content_asset_access_events_content_assets_tenant_id_asset_~",
columns: x => new { x.tenant_id, x.asset_id },
principalTable: "content_assets",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_asset_access_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_content_asset_access_events_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "content_asset_security_scan_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
asset_id = table.Column<Guid>(type: "uuid", nullable: true),
provider = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
scan_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
risk_level = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
issue_codes = table.Column<string[]>(type: "text[]", nullable: false, defaultValueSql: "'{}'::text[]"),
details = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_content_asset_security_scan_events", x => x.id);
table.UniqueConstraint("ak_content_asset_security_scan_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_content_asset_security_scan_events_content_assets_tenant_id~",
columns: x => new { x.tenant_id, x.asset_id },
principalTable: "content_assets",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_content_asset_security_scan_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "question_bank_grants",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
source_question_bank_id = table.Column<Guid>(type: "uuid", nullable: false),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
grant_scope = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
allowed_plan_codes = table.Column<string[]>(type: "text[]", nullable: false, defaultValueSql: "'{}'::text[]"),
allowed_tenant_ids = table.Column<Guid[]>(type: "uuid[]", nullable: false, defaultValueSql: "'{}'::uuid[]"),
allowed_region_ids = table.Column<Guid[]>(type: "uuid[]", nullable: false, defaultValueSql: "'{}'::uuid[]"),
allowed_subject_ids = table.Column<Guid[]>(type: "uuid[]", nullable: false, defaultValueSql: "'{}'::uuid[]"),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
starts_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
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_question_bank_grants", x => x.id);
table.ForeignKey(
name: "fk_question_bank_grants_question_banks_source_question_bank_id",
column: x => x.source_question_bank_id,
principalTable: "question_banks",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_question_bank_grants_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_question_bank_grants_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "tenant_question_bank_adoptions",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
source_question_bank_id = table.Column<Guid>(type: "uuid", nullable: false),
grant_id = table.Column<Guid>(type: "uuid", nullable: true),
target_question_bank_id = table.Column<Guid>(type: "uuid", nullable: true),
target_entry_id = table.Column<Guid>(type: "uuid", nullable: true),
target_collection_id = table.Column<Guid>(type: "uuid", nullable: true),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
updated_by = table.Column<Guid>(type: "uuid", nullable: true),
adoption_mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
sync_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
source_snapshot = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
copied_question_count = table.Column<int>(type: "integer", nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
last_synced_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_tenant_question_bank_adoptions", x => x.id);
table.UniqueConstraint("ak_tenant_question_bank_adoptions_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0");
table.ForeignKey(
name: "fk_tenant_question_bank_adoptions_content_entries_tenant_id_ta~",
columns: x => new { x.tenant_id, x.target_entry_id },
principalTable: "content_entries",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_tenant_question_bank_adoptions_question_bank_grants_grant_id",
column: x => x.grant_id,
principalTable: "question_bank_grants",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_question_bank_adoptions_question_banks_source_questi~",
column: x => x.source_question_bank_id,
principalTable: "question_banks",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_question_bank_adoptions_question_banks_tenant_id_tar~",
columns: x => new { x.tenant_id, x.target_question_bank_id },
principalTable: "question_banks",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_tenant_question_bank_adoptions_question_collections_tenant_~",
columns: x => new { x.tenant_id, x.target_collection_id },
principalTable: "question_collections",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_tenant_question_bank_adoptions_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_question_bank_adoptions_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_tenant_question_bank_adoptions_users_updated_by",
column: x => x.updated_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateIndex(
name: "ix_content_assets_tenant_id_security_scan_status_upload_status~",
table: "content_assets",
columns: new[] { "tenant_id", "security_scan_status", "upload_status", "status", "updated_at" });
migrationBuilder.CreateIndex(
name: "ix_ai_recommendation_reports_tenant_id_region_id_created_at",
table: "ai_recommendation_reports",
columns: new[] { "tenant_id", "region_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_ai_recommendation_reports_tenant_id_user_id_created_at",
table: "ai_recommendation_reports",
columns: new[] { "tenant_id", "user_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_ai_recommendation_reports_user_id",
table: "ai_recommendation_reports",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_content_asset_access_events_tenant_id_access_type_result_cr~",
table: "content_asset_access_events",
columns: new[] { "tenant_id", "access_type", "result", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_content_asset_access_events_tenant_id_asset_id_created_at",
table: "content_asset_access_events",
columns: new[] { "tenant_id", "asset_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_content_asset_access_events_tenant_id_user_id_created_at",
table: "content_asset_access_events",
columns: new[] { "tenant_id", "user_id", "created_at" },
filter: "user_id is not null");
migrationBuilder.CreateIndex(
name: "ix_content_asset_access_events_user_id",
table: "content_asset_access_events",
column: "user_id");
migrationBuilder.CreateIndex(
name: "ix_content_asset_security_scan_events_tenant_id_asset_id_creat~",
table: "content_asset_security_scan_events",
columns: new[] { "tenant_id", "asset_id", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_content_asset_security_scan_events_tenant_id_scan_status_ri~",
table: "content_asset_security_scan_events",
columns: new[] { "tenant_id", "scan_status", "risk_level", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_question_bank_grants_allowed_plan_codes",
table: "question_bank_grants",
column: "allowed_plan_codes")
.Annotation("Npgsql:IndexMethod", "gin");
migrationBuilder.CreateIndex(
name: "ix_question_bank_grants_allowed_tenant_ids",
table: "question_bank_grants",
column: "allowed_tenant_ids")
.Annotation("Npgsql:IndexMethod", "gin");
migrationBuilder.CreateIndex(
name: "ix_question_bank_grants_created_by",
table: "question_bank_grants",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_question_bank_grants_source_question_bank_id_status_starts_~",
table: "question_bank_grants",
columns: new[] { "source_question_bank_id", "status", "starts_at", "expires_at" });
migrationBuilder.CreateIndex(
name: "ix_question_bank_grants_updated_by",
table: "question_bank_grants",
column: "updated_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_created_by",
table: "tenant_question_bank_adoptions",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_grant_id",
table: "tenant_question_bank_adoptions",
column: "grant_id");
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_source_question_bank_id",
table: "tenant_question_bank_adoptions",
column: "source_question_bank_id");
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_tenant_id_source_question_ba~",
table: "tenant_question_bank_adoptions",
columns: new[] { "tenant_id", "source_question_bank_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_tenant_id_status_updated_at",
table: "tenant_question_bank_adoptions",
columns: new[] { "tenant_id", "status", "updated_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_tenant_id_target_collection_~",
table: "tenant_question_bank_adoptions",
columns: new[] { "tenant_id", "target_collection_id" });
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_tenant_id_target_entry_id",
table: "tenant_question_bank_adoptions",
columns: new[] { "tenant_id", "target_entry_id" });
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_tenant_id_target_question_ba~",
table: "tenant_question_bank_adoptions",
columns: new[] { "tenant_id", "target_question_bank_id" });
migrationBuilder.CreateIndex(
name: "ix_tenant_question_bank_adoptions_updated_by",
table: "tenant_question_bank_adoptions",
column: "updated_by");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ai_recommendation_reports");
migrationBuilder.DropTable(
name: "content_asset_access_events");
migrationBuilder.DropTable(
name: "content_asset_security_scan_events");
migrationBuilder.DropTable(
name: "tenant_question_bank_adoptions");
migrationBuilder.DropTable(
name: "question_bank_grants");
migrationBuilder.DropIndex(
name: "ix_content_assets_tenant_id_security_scan_status_upload_status~",
table: "content_assets");
migrationBuilder.DropColumn(
name: "security_scan_provider",
table: "content_assets");
migrationBuilder.DropColumn(
name: "security_scan_status",
table: "content_assets");
migrationBuilder.DropColumn(
name: "security_scan_summary",
table: "content_assets");
migrationBuilder.DropColumn(
name: "security_scanned_at",
table: "content_assets");
}
}
}

View File

@@ -1,633 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddPlatformBillingDunningAndAuditPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "platform_audit_alert_rules",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: true),
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
enabled = table.Column<bool>(type: "boolean", nullable: false),
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
action_patterns = table.Column<string[]>(type: "text[]", nullable: false, defaultValueSql: "'{}'::text[]"),
target_types = table.Column<string[]>(type: "text[]", nullable: false, defaultValueSql: "'{}'::text[]"),
conditions = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
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_platform_audit_alert_rules", x => x.id);
table.ForeignKey(
name: "fk_platform_audit_alert_rules_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "platform_dunning_notification_channels",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
channel_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
enabled = table.Column<bool>(type: "boolean", nullable: false),
provider = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
webhook_url = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: false),
secret_ref = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
reminder_types = table.Column<string[]>(type: "text[]", nullable: false, defaultValueSql: "array['overdue', 'final_notice']::text[]"),
reminder_channels = table.Column<string[]>(type: "text[]", nullable: false, defaultValueSql: "array['internal']::text[]"),
min_reminder_level = table.Column<int>(type: "integer", nullable: false),
tenant_ids = table.Column<Guid[]>(type: "uuid[]", nullable: false, defaultValueSql: "'{}'::uuid[]"),
timeout_seconds = table.Column<int>(type: "integer", nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
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_platform_dunning_notification_channels", x => x.id);
table.CheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20");
table.CheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60");
});
migrationBuilder.CreateTable(
name: "platform_saas_plans",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
description = table.Column<string>(type: "text", nullable: true),
billing_cycle = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
base_amount_cents = table.Column<int>(type: "integer", nullable: false),
currency = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: false),
included_quotas = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
overage_prices = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
feature_flags = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
sort_order = table.Column<int>(type: "integer", 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_platform_saas_plans", x => x.id);
table.CheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0");
});
migrationBuilder.CreateTable(
name: "tenant_billing_profiles",
columns: table => new
{
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
billing_name = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
tax_id = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
contact_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
contact_phone = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
contact_email = table.Column<string>(type: "citext", maxLength: 300, nullable: true),
billing_address = table.Column<string>(type: "text", nullable: true),
invoice_title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: true),
invoice_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
bank_name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
bank_account_masked = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
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_tenant_billing_profiles", x => x.tenant_id);
table.ForeignKey(
name: "fk_tenant_billing_profiles_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_invoices",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
invoice_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
invoice_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
currency = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: false),
subtotal_cents = table.Column<int>(type: "integer", nullable: false),
discount_cents = table.Column<int>(type: "integer", nullable: false),
tax_cents = table.Column<int>(type: "integer", nullable: false),
total_cents = table.Column<int>(type: "integer", nullable: false),
paid_cents = table.Column<int>(type: "integer", nullable: false),
balance_cents = table.Column<int>(type: "integer", nullable: false),
billing_period_start = table.Column<DateOnly>(type: "date", nullable: true),
billing_period_end = table.Column<DateOnly>(type: "date", nullable: true),
due_date = table.Column<DateOnly>(type: "date", nullable: true),
issued_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
paid_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
note = table.Column<string>(type: "text", nullable: true),
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_tenant_invoices", x => x.id);
table.UniqueConstraint("ak_tenant_invoices_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0");
table.CheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start");
table.ForeignKey(
name: "fk_tenant_invoices_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_invoices_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "platform_audit_alerts",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
rule_id = table.Column<Guid>(type: "uuid", nullable: false),
audit_log_id = table.Column<Guid>(type: "uuid", nullable: false),
tenant_id = table.Column<Guid>(type: "uuid", nullable: true),
acknowledged_by = table.Column<Guid>(type: "uuid", nullable: true),
resolved_by = table.Column<Guid>(type: "uuid", nullable: true),
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
action = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
target_type = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
target_id = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
title = table.Column<string>(type: "character varying(300)", maxLength: 300, nullable: false),
summary = table.Column<string>(type: "text", nullable: true),
details = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
first_seen_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
last_seen_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
acknowledged_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
resolved_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
resolution_note = table.Column<string>(type: "text", nullable: true),
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_platform_audit_alerts", x => x.id);
table.ForeignKey(
name: "fk_platform_audit_alerts_audit_logs_audit_log_id",
column: x => x.audit_log_id,
principalTable: "audit_logs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_platform_audit_alerts_platform_audit_alert_rules_rule_id",
column: x => x.rule_id,
principalTable: "platform_audit_alert_rules",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_platform_audit_alerts_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_platform_audit_alerts_users_acknowledged_by",
column: x => x.acknowledged_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "fk_platform_audit_alerts_users_resolved_by",
column: x => x.resolved_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "tenant_invoice_items",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
invoice_id = table.Column<Guid>(type: "uuid", nullable: false),
item_type = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
item_ref_id = table.Column<Guid>(type: "uuid", nullable: true),
description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
quantity = table.Column<decimal>(type: "numeric(12,2)", precision: 12, scale: 2, nullable: false),
unit_amount_cents = table.Column<int>(type: "integer", nullable: false),
amount_cents = table.Column<int>(type: "integer", nullable: false),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_tenant_invoice_items", x => x.id);
table.UniqueConstraint("ak_tenant_invoice_items_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0");
table.CheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0");
table.ForeignKey(
name: "fk_tenant_invoice_items_tenant_invoices_tenant_id_invoice_id",
columns: x => new { x.tenant_id, x.invoice_id },
principalTable: "tenant_invoices",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_invoice_items_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_invoice_payments",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
invoice_id = table.Column<Guid>(type: "uuid", nullable: false),
received_by = table.Column<Guid>(type: "uuid", nullable: true),
payment_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
method = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
amount_cents = table.Column<int>(type: "integer", nullable: false),
paid_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
provider_trade_no = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
raw_payload = 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_tenant_invoice_payments", x => x.id);
table.UniqueConstraint("ak_tenant_invoice_payments_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0");
table.ForeignKey(
name: "fk_tenant_invoice_payments_tenant_invoices_tenant_id_invoice_id",
columns: x => new { x.tenant_id, x.invoice_id },
principalTable: "tenant_invoices",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_invoice_payments_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_invoice_payments_users_received_by",
column: x => x.received_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "tenant_invoice_reminders",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
invoice_id = table.Column<Guid>(type: "uuid", nullable: false),
created_by = table.Column<Guid>(type: "uuid", nullable: true),
reminder_type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
channel = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
reminder_date = table.Column<DateOnly>(type: "date", nullable: false),
reminder_level = table.Column<int>(type: "integer", nullable: false),
due_date = table.Column<DateOnly>(type: "date", nullable: true),
balance_cents_snapshot = table.Column<int>(type: "integer", nullable: false),
message = table.Column<string>(type: "text", nullable: true),
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
sent_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
acknowledged_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
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_tenant_invoice_reminders", x => x.id);
table.UniqueConstraint("ak_tenant_invoice_reminders_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0");
table.CheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20");
table.ForeignKey(
name: "fk_tenant_invoice_reminders_tenant_invoices_tenant_id_invoice_~",
columns: x => new { x.tenant_id, x.invoice_id },
principalTable: "tenant_invoices",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_invoice_reminders_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_invoice_reminders_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateTable(
name: "platform_dunning_notification_events",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
channel_id = table.Column<Guid>(type: "uuid", nullable: false),
reminder_id = table.Column<Guid>(type: "uuid", nullable: false),
invoice_id = table.Column<Guid>(type: "uuid", nullable: false),
provider = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
attempts = table.Column<int>(type: "integer", nullable: false),
scheduled_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
next_attempt_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
last_attempt_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
sent_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
last_error = table.Column<string>(type: "text", nullable: true),
last_http_code = table.Column<int>(type: "integer", nullable: true),
last_response_summary = table.Column<string>(type: "text", nullable: true),
request_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_platform_dunning_notification_events", x => x.id);
table.UniqueConstraint("ak_platform_dunning_notification_events_tenant_id_id", x => new { x.tenant_id, x.id });
table.CheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0");
table.ForeignKey(
name: "fk_platform_dunning_notification_events_platform_dunning_notif~",
column: x => x.channel_id,
principalTable: "platform_dunning_notification_channels",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_platform_dunning_notification_events_tenant_invoice_reminde~",
columns: x => new { x.tenant_id, x.reminder_id },
principalTable: "tenant_invoice_reminders",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_platform_dunning_notification_events_tenant_invoices_tenant~",
columns: x => new { x.tenant_id, x.invoice_id },
principalTable: "tenant_invoices",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_platform_dunning_notification_events_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alert_rules_code",
table: "platform_audit_alert_rules",
column: "code",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alert_rules_enabled_severity_code",
table: "platform_audit_alert_rules",
columns: new[] { "enabled", "severity", "code" });
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alert_rules_tenant_id",
table: "platform_audit_alert_rules",
column: "tenant_id");
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alerts_acknowledged_by",
table: "platform_audit_alerts",
column: "acknowledged_by");
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alerts_audit_log_id",
table: "platform_audit_alerts",
column: "audit_log_id");
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alerts_resolved_by",
table: "platform_audit_alerts",
column: "resolved_by");
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alerts_rule_id_audit_log_id",
table: "platform_audit_alerts",
columns: new[] { "rule_id", "audit_log_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alerts_status_severity_created_at",
table: "platform_audit_alerts",
columns: new[] { "status", "severity", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_platform_audit_alerts_tenant_id_status_created_at",
table: "platform_audit_alerts",
columns: new[] { "tenant_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_channels_channel_code",
table: "platform_dunning_notification_channels",
column: "channel_code",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_channels_enabled_min_reminder~",
table: "platform_dunning_notification_channels",
columns: new[] { "enabled", "min_reminder_level", "channel_code" });
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_events_channel_id_reminder_id",
table: "platform_dunning_notification_events",
columns: new[] { "channel_id", "reminder_id" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_events_invoice_id_status_crea~",
table: "platform_dunning_notification_events",
columns: new[] { "invoice_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_events_reminder_id_status_cre~",
table: "platform_dunning_notification_events",
columns: new[] { "reminder_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_events_status_next_attempt_at~",
table: "platform_dunning_notification_events",
columns: new[] { "status", "next_attempt_at", "scheduled_at", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_events_tenant_id_invoice_id",
table: "platform_dunning_notification_events",
columns: new[] { "tenant_id", "invoice_id" });
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_events_tenant_id_reminder_id",
table: "platform_dunning_notification_events",
columns: new[] { "tenant_id", "reminder_id" });
migrationBuilder.CreateIndex(
name: "ix_platform_dunning_notification_events_tenant_id_status_creat~",
table: "platform_dunning_notification_events",
columns: new[] { "tenant_id", "status", "created_at" });
migrationBuilder.CreateIndex(
name: "ix_platform_saas_plans_code",
table: "platform_saas_plans",
column: "code",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_platform_saas_plans_status_sort_order",
table: "platform_saas_plans",
columns: new[] { "status", "sort_order" });
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_items_invoice_id",
table: "tenant_invoice_items",
column: "invoice_id");
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_items_tenant_id_invoice_id",
table: "tenant_invoice_items",
columns: new[] { "tenant_id", "invoice_id" });
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_payments_invoice_id_status",
table: "tenant_invoice_payments",
columns: new[] { "invoice_id", "status" });
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_payments_payment_no",
table: "tenant_invoice_payments",
column: "payment_no",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_payments_received_by",
table: "tenant_invoice_payments",
column: "received_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_payments_tenant_id_invoice_id",
table: "tenant_invoice_payments",
columns: new[] { "tenant_id", "invoice_id" });
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_reminders_created_by",
table: "tenant_invoice_reminders",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_reminders_invoice_id_reminder_date",
table: "tenant_invoice_reminders",
columns: new[] { "invoice_id", "reminder_date" });
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_reminders_tenant_id_invoice_id_reminder_type~",
table: "tenant_invoice_reminders",
columns: new[] { "tenant_id", "invoice_id", "reminder_type", "channel", "reminder_date" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_invoice_reminders_tenant_id_status_reminder_date",
table: "tenant_invoice_reminders",
columns: new[] { "tenant_id", "status", "reminder_date" });
migrationBuilder.CreateIndex(
name: "ix_tenant_invoices_created_by",
table: "tenant_invoices",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_invoices_invoice_no",
table: "tenant_invoices",
column: "invoice_no",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_invoices_tenant_id_billing_period_start_billing_peri~",
table: "tenant_invoices",
columns: new[] { "tenant_id", "billing_period_start", "billing_period_end" },
unique: true,
filter: "invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'");
migrationBuilder.CreateIndex(
name: "ix_tenant_invoices_tenant_id_status_due_date",
table: "tenant_invoices",
columns: new[] { "tenant_id", "status", "due_date" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "platform_audit_alerts");
migrationBuilder.DropTable(
name: "platform_dunning_notification_events");
migrationBuilder.DropTable(
name: "platform_saas_plans");
migrationBuilder.DropTable(
name: "tenant_billing_profiles");
migrationBuilder.DropTable(
name: "tenant_invoice_items");
migrationBuilder.DropTable(
name: "tenant_invoice_payments");
migrationBuilder.DropTable(
name: "platform_audit_alert_rules");
migrationBuilder.DropTable(
name: "platform_dunning_notification_channels");
migrationBuilder.DropTable(
name: "tenant_invoice_reminders");
migrationBuilder.DropTable(
name: "tenant_invoices");
}
}
}

View File

@@ -1,148 +0,0 @@
using System;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddPocketBaseImportAuditPersistence : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "pb_import_runs",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
source_name = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
source_kind = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
stats = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
started_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
finished_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_pb_import_runs", x => x.id);
table.UniqueConstraint("ak_pb_import_runs_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_pb_import_runs_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "pb_import_issues",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
run_id = table.Column<Guid>(type: "uuid", nullable: true),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
collection_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
legacy_id = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
issue_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
message = table.Column<string>(type: "text", nullable: false),
field_path = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
raw_value_sample = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_pb_import_issues", x => x.id);
table.UniqueConstraint("ak_pb_import_issues_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_pb_import_issues_pb_import_runs_tenant_id_run_id",
columns: x => new { x.tenant_id, x.run_id },
principalTable: "pb_import_runs",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_pb_import_issues_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "pb_raw_records",
columns: table => new
{
run_id = table.Column<Guid>(type: "uuid", nullable: false),
collection_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
legacy_id = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
record = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
normalized = table.Column<bool>(type: "boolean", nullable: false),
errors = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
imported_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
},
constraints: table =>
{
table.PrimaryKey("pk_pb_raw_records", x => new { x.run_id, x.collection_name, x.legacy_id });
table.ForeignKey(
name: "fk_pb_raw_records_pb_import_runs_tenant_id_run_id",
columns: x => new { x.tenant_id, x.run_id },
principalTable: "pb_import_runs",
principalColumns: new[] { "tenant_id", "id" },
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_pb_raw_records_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_pb_import_issues_run_id_severity_collection_name",
table: "pb_import_issues",
columns: new[] { "run_id", "severity", "collection_name" });
migrationBuilder.CreateIndex(
name: "ix_pb_import_issues_tenant_id_collection_name_legacy_id",
table: "pb_import_issues",
columns: new[] { "tenant_id", "collection_name", "legacy_id" });
migrationBuilder.CreateIndex(
name: "ix_pb_import_issues_tenant_id_run_id",
table: "pb_import_issues",
columns: new[] { "tenant_id", "run_id" });
migrationBuilder.CreateIndex(
name: "ix_pb_import_runs_tenant_id_status_started_at",
table: "pb_import_runs",
columns: new[] { "tenant_id", "status", "started_at" });
migrationBuilder.CreateIndex(
name: "ix_pb_raw_records_tenant_id_collection_name_legacy_id",
table: "pb_raw_records",
columns: new[] { "tenant_id", "collection_name", "legacy_id" });
migrationBuilder.CreateIndex(
name: "ix_pb_raw_records_tenant_id_run_id",
table: "pb_raw_records",
columns: new[] { "tenant_id", "run_id" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "pb_import_issues");
migrationBuilder.DropTable(
name: "pb_raw_records");
migrationBuilder.DropTable(
name: "pb_import_runs");
}
}
}

View File

@@ -13,8 +13,8 @@ using Tiku.Infrastructure.Persistence;
namespace Tiku.Infrastructure.Persistence.Migrations
{
[DbContext(typeof(TikuDbContext))]
[Migration("20260725220429_AddPocketBaseImportAuditPersistence")]
partial class AddPocketBaseImportAuditPersistence
[Migration("20260725220742_InitialSchema")]
partial class InitialSchema
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)

File diff suppressed because it is too large Load Diff