Files
gongxue-base/supabase/migrations/202606210003_learning_content_import_extensions.sql
2026-06-21 21:54:43 +08:00

181 lines
7.4 KiB
SQL

create table if not exists public.exam_dates (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
region_id uuid references public.regions(id) on delete set null,
school_id uuid references public.schools(id) on delete set null,
legacy_id text,
exam_name text not null,
exam_date date,
exam_type text,
sort_order integer not null default 0,
is_active boolean not null default true,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, legacy_id)
);
create table if not exists public.question_type_groups (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
subject_id uuid references public.subjects(id) on delete cascade,
legacy_id text,
legacy_subject_id text,
display_name text not null,
types jsonb not null default '[]'::jsonb,
sort_order integer not null default 0,
is_active boolean not null default true,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, legacy_id)
);
create table if not exists public.subject_shares (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
source_subject_id uuid references public.subjects(id) on delete cascade,
target_subject_id uuid references public.subjects(id) on delete cascade,
legacy_id text,
legacy_source_subject_id text,
legacy_target_subject_id text,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, legacy_id)
);
create table if not exists public.recent_practices (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
user_id uuid references public.platform_users(id) on delete cascade,
legacy_id text,
practice_type text,
target_legacy_id text,
target_name text,
progress integer not null default 0,
color text,
last_access_at timestamptz,
last_practice_at timestamptz,
metadata jsonb not null default '{}'::jsonb,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, legacy_id)
);
create table if not exists public.user_word_progress (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
user_id uuid references public.platform_users(id) on delete cascade,
word_id uuid references public.vocabulary_words(id) on delete cascade,
legacy_id text,
legacy_user_id text,
legacy_word_id text,
status text not null default 'new' check (status in ('new', 'learning', 'mastered', 'reviewing')),
correct_count integer not null default 0,
wrong_count integer not null default 0,
last_review_date timestamptz,
next_review_date timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, legacy_id),
unique (tenant_id, user_id, word_id)
);
create table if not exists public.user_word_favorites (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
user_id uuid references public.platform_users(id) on delete cascade,
word_id uuid references public.vocabulary_words(id) on delete cascade,
legacy_id text,
legacy_user_id text,
legacy_word_id text,
note text,
favorited_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, legacy_id),
unique (tenant_id, user_id, word_id)
);
create table if not exists public.content_assets (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
legacy_id text,
asset_key text,
title text,
category text,
description text,
file_name text,
cdn_url text,
is_public boolean not null default false,
metadata jsonb not null default '{}'::jsonb,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, legacy_id)
);
create table if not exists public.dashboard_daily_stats (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
legacy_id text,
stat_date date not null,
region_id uuid references public.regions(id) on delete set null,
legacy_region_id text,
new_users integer not null default 0,
new_questions integer not null default 0,
new_orders integer not null default 0,
new_revenue_cents integer not null default 0,
active_users integer not null default 0,
rebuilt_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, stat_date, legacy_region_id)
);
create table if not exists public.revenue_daily_stats (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
legacy_id text,
stat_date date not null,
region_id uuid references public.regions(id) on delete set null,
legacy_region_id text,
sale_type text,
real_revenue_cents integer not null default 0,
order_count integer not null default 0,
code_count integer not null default 0,
code_used integer not null default 0,
code_estimated_cents integer not null default 0,
estimated_revenue_cents integer not null default 0,
rebuilt_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, stat_date, legacy_region_id, sale_type)
);
create index if not exists idx_exam_dates_tenant_region on public.exam_dates(tenant_id, region_id, exam_date);
create index if not exists idx_question_type_groups_subject on public.question_type_groups(tenant_id, subject_id, sort_order);
create index if not exists idx_recent_practices_user on public.recent_practices(tenant_id, user_id, last_practice_at desc);
create index if not exists idx_user_word_progress_user on public.user_word_progress(tenant_id, user_id, status);
create index if not exists idx_content_assets_tenant_key on public.content_assets(tenant_id, asset_key);
create index if not exists idx_dashboard_daily_stats_date on public.dashboard_daily_stats(tenant_id, stat_date desc);
create index if not exists idx_revenue_daily_stats_date on public.revenue_daily_stats(tenant_id, stat_date desc);
do $$
declare
table_name text;
begin
foreach table_name in array array[
'exam_dates', 'question_type_groups', 'subject_shares', 'recent_practices',
'user_word_progress', 'user_word_favorites', 'content_assets',
'dashboard_daily_stats', 'revenue_daily_stats'
]
loop
execute format('alter table public.%I enable row level security', table_name);
execute format('drop policy if exists tenant_isolation on public.%I', table_name);
execute format(
'create policy tenant_isolation on public.%I for all using (tenant_id = app.current_tenant_id() or app.is_platform_admin()) with check (tenant_id = app.current_tenant_id() or app.is_platform_admin())',
table_name
);
execute format('drop trigger if exists set_updated_at on public.%I', table_name);
execute format('create trigger set_updated_at before update on public.%I for each row execute function app.touch_updated_at()', table_name);
end loop;
end $$;