feat: add tenant student operations

This commit is contained in:
Codex
2026-06-29 01:16:25 +08:00
parent 61240c5833
commit 726d090a8e
12 changed files with 908 additions and 24 deletions

View File

@@ -0,0 +1,92 @@
create table if not exists public.tenant_student_notes (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
student_user_id uuid not null references public.platform_users(id) on delete cascade,
note_type text not null default 'general'
check (note_type in ('general', 'learning', 'service', 'sales', 'risk', 'follow_up')),
content text not null,
visibility text not null default 'tenant_staff'
check (visibility in ('tenant_staff', 'class_staff', 'author_only')),
is_pinned boolean not null default false,
metadata jsonb not null default '{}'::jsonb,
created_by uuid references public.platform_users(id) on delete set null,
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()
);
comment on table public.tenant_student_notes is
'Tenant-scoped student operation notes for teachers, class advisers, operators and sales follow-up.';
create index if not exists idx_student_notes_tenant_student
on public.tenant_student_notes(tenant_id, student_user_id, is_pinned desc, created_at desc);
create index if not exists idx_student_notes_tenant_creator
on public.tenant_student_notes(tenant_id, created_by, created_at desc)
where created_by is not null;
create table if not exists public.tenant_student_followups (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
student_user_id uuid not null references public.platform_users(id) on delete cascade,
assigned_to_user_id uuid references public.platform_users(id) on delete set null,
class_id uuid,
title text not null,
description text,
followup_type text not null default 'learning'
check (followup_type in ('learning', 'service', 'sales', 'renewal', 'risk', 'custom')),
priority text not null default 'normal'
check (priority in ('low', 'normal', 'high', 'urgent')),
status text not null default 'open'
check (status in ('open', 'in_progress', 'done', 'cancelled')),
due_at timestamptz,
completed_at timestamptz,
completed_by uuid references public.platform_users(id) on delete set null,
metadata jsonb not null default '{}'::jsonb,
created_by uuid references public.platform_users(id) on delete set null,
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(),
foreign key (class_id) references public.tenant_classes(id) on delete set null
);
comment on table public.tenant_student_followups is
'Tenant-scoped student follow-up tasks for learning supervision, service, sales and renewal operations.';
create index if not exists idx_student_followups_tenant_student
on public.tenant_student_followups(tenant_id, student_user_id, status, due_at nulls last, created_at desc);
create index if not exists idx_student_followups_tenant_assignee
on public.tenant_student_followups(tenant_id, assigned_to_user_id, status, due_at nulls last)
where assigned_to_user_id is not null;
create index if not exists idx_student_followups_tenant_class
on public.tenant_student_followups(tenant_id, class_id, status, due_at nulls last)
where class_id is not null;
alter table public.tenant_student_notes enable row level security;
alter table public.tenant_student_followups enable row level security;
drop policy if exists tenant_isolation on public.tenant_student_notes;
create policy tenant_isolation on public.tenant_student_notes
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 policy if exists tenant_isolation on public.tenant_student_followups;
create policy tenant_isolation on public.tenant_student_followups
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_student_notes;
create trigger set_updated_at
before update on public.tenant_student_notes
for each row
execute function app.touch_updated_at();
drop trigger if exists set_updated_at on public.tenant_student_followups;
create trigger set_updated_at
before update on public.tenant_student_followups
for each row
execute function app.touch_updated_at();