it0/packages/shared/database/migrations/010-create-notification-cam...

78 lines
3.8 KiB
SQL

-- ============================================================
-- Migration 010: Notification Campaigns + Read Event Log
-- Phase 3: Campaign scheduling + analytics
-- ============================================================
-- 1. Notification campaigns
-- A campaign is a scheduled or recurring notification push job
CREATE TABLE IF NOT EXISTS public.notification_campaigns (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(200) NOT NULL,
description TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
-- DRAFT | SCHEDULED | RUNNING | COMPLETED | CANCELLED | FAILED
-- Linked notification template (optional: can create ad-hoc body)
notification_id UUID REFERENCES public.notifications(id) ON DELETE SET NULL,
-- Schedule
schedule_type VARCHAR(20) NOT NULL DEFAULT 'ONCE',
-- ONCE | RECURRING
scheduled_at TIMESTAMPTZ, -- for ONCE: exact send time
cron_expr VARCHAR(100), -- for RECURRING: cron expression
timezone VARCHAR(50) NOT NULL DEFAULT 'UTC',
next_run_at TIMESTAMPTZ, -- computed by scheduler
last_run_at TIMESTAMPTZ,
-- Execution stats
target_count INT NOT NULL DEFAULT 0, -- estimated / actual target size
sent_count INT NOT NULL DEFAULT 0,
read_count INT NOT NULL DEFAULT 0,
created_by VARCHAR(100),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_campaigns_status ON public.notification_campaigns (status);
CREATE INDEX IF NOT EXISTS idx_campaigns_scheduled_at ON public.notification_campaigns (scheduled_at)
WHERE status IN ('SCHEDULED', 'RUNNING');
CREATE INDEX IF NOT EXISTS idx_campaigns_next_run ON public.notification_campaigns (next_run_at)
WHERE status = 'SCHEDULED';
-- 2. Campaign execution log
-- Records each run of a campaign (for RECURRING campaigns, one row per run)
CREATE TABLE IF NOT EXISTS public.campaign_execution_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
campaign_id UUID NOT NULL REFERENCES public.notification_campaigns(id) ON DELETE CASCADE,
notification_id UUID REFERENCES public.notifications(id) ON DELETE SET NULL,
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
finished_at TIMESTAMPTZ,
status VARCHAR(20) NOT NULL DEFAULT 'RUNNING',
-- RUNNING | COMPLETED | FAILED
target_count INT NOT NULL DEFAULT 0,
sent_count INT NOT NULL DEFAULT 0,
error TEXT
);
CREATE INDEX IF NOT EXISTS idx_cel_campaign ON public.campaign_execution_log (campaign_id);
-- 3. Notification delivery event log
-- Detailed per-notification read events (for analytics / funnel)
CREATE TABLE IF NOT EXISTS public.notification_event_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
notification_id UUID NOT NULL REFERENCES public.notifications(id) ON DELETE CASCADE,
campaign_id UUID REFERENCES public.notification_campaigns(id) ON DELETE SET NULL,
event_type VARCHAR(30) NOT NULL,
-- DELIVERED | READ | CLICKED | DISMISSED
user_id VARCHAR(100) NOT NULL,
tenant_id VARCHAR(100) NOT NULL,
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
metadata JSONB DEFAULT '{}'::jsonb
);
CREATE INDEX IF NOT EXISTS idx_nel_notification ON public.notification_event_log (notification_id);
CREATE INDEX IF NOT EXISTS idx_nel_campaign ON public.notification_event_log (campaign_id) WHERE campaign_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_nel_user ON public.notification_event_log (user_id);
CREATE INDEX IF NOT EXISTS idx_nel_occurred ON public.notification_event_log (occurred_at);