Files
gongxue-base/supabase/migrations/202606290020_tenant_theme_templates.sql
2026-06-29 18:05:30 +08:00

130 lines
4.5 KiB
SQL

create table if not exists public.tenant_theme_templates (
id uuid primary key default gen_random_uuid(),
code text not null unique,
name text not null,
description text,
preview_image_url text,
theme jsonb not null default '{}'::jsonb,
public_assets jsonb not null default '{}'::jsonb,
status text not null default 'active' check (status in ('active', 'disabled')),
sort_order integer not null default 100,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create table if not exists public.tenant_theme_configs (
tenant_id uuid primary key references public.tenants(id) on delete cascade,
active_template_code text references public.tenant_theme_templates(code) on delete set null,
active_theme jsonb not null default '{}'::jsonb,
active_public_assets jsonb not null default '{}'::jsonb,
draft_template_code text references public.tenant_theme_templates(code) on delete set null,
draft_theme jsonb not null default '{}'::jsonb,
draft_public_assets jsonb not null default '{}'::jsonb,
status text not null default 'published' check (status in ('draft', 'published')),
published_at timestamptz,
published_by uuid references public.platform_users(id) on delete set null,
draft_updated_by uuid references public.platform_users(id) on delete set null,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create index if not exists idx_theme_templates_status_sort
on public.tenant_theme_templates(status, sort_order, code);
alter table public.tenant_theme_templates enable row level security;
alter table public.tenant_theme_configs enable row level security;
drop policy if exists theme_templates_public_read on public.tenant_theme_templates;
create policy theme_templates_public_read on public.tenant_theme_templates
for select
using (status = 'active' or app.is_platform_admin());
drop policy if exists tenant_isolation on public.tenant_theme_configs;
create policy tenant_isolation on public.tenant_theme_configs
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());
drop trigger if exists set_updated_at on public.tenant_theme_templates;
create trigger set_updated_at
before update on public.tenant_theme_templates
for each row
execute function app.touch_updated_at();
drop trigger if exists set_updated_at on public.tenant_theme_configs;
create trigger set_updated_at
before update on public.tenant_theme_configs
for each row
execute function app.touch_updated_at();
insert into public.tenant_theme_templates (code, name, description, theme, public_assets, sort_order)
values
(
'classic',
'经典蓝',
'稳重、清晰、适合默认题库和机构后台。',
'{
"mode":"light",
"primaryColor":"#2563eb",
"accentColor":"#0f766e",
"backgroundColor":"#f8fafc",
"surfaceColor":"#ffffff",
"textColor":"#0f172a",
"mutedColor":"#64748b",
"borderRadius":8,
"buttonRadius":8,
"fontFamily":"system",
"layoutDensity":"comfortable"
}'::jsonb,
'{"iconSet":"classic","shareCardStyle":"clean"}'::jsonb,
10
),
(
'focus',
'专注绿',
'低干扰学习主题,适合学生端刷题和背单词场景。',
'{
"mode":"light",
"primaryColor":"#0f766e",
"accentColor":"#7c3aed",
"backgroundColor":"#f5fbf8",
"surfaceColor":"#ffffff",
"textColor":"#10201b",
"mutedColor":"#55706a",
"borderRadius":8,
"buttonRadius":8,
"fontFamily":"system",
"layoutDensity":"compact"
}'::jsonb,
'{"iconSet":"focus","shareCardStyle":"study"}'::jsonb,
20
),
(
'high-contrast',
'高对比',
'强化标题、按钮和导航对比度,适合强运营入口和大屏后台。',
'{
"mode":"light",
"primaryColor":"#111827",
"accentColor":"#f59e0b",
"backgroundColor":"#f3f4f6",
"surfaceColor":"#ffffff",
"textColor":"#030712",
"mutedColor":"#4b5563",
"borderRadius":6,
"buttonRadius":6,
"fontFamily":"system",
"layoutDensity":"dense"
}'::jsonb,
'{"iconSet":"contrast","shareCardStyle":"bold"}'::jsonb,
30
)
on conflict (code)
do update set name = excluded.name,
description = excluded.description,
theme = excluded.theme,
public_assets = excluded.public_assets,
sort_order = excluded.sort_order,
status = 'active',
updated_at = now();