forked from wangziqi/gongxue-base
181 lines
7.4 KiB
SQL
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 $$;
|