78 lines
3.8 KiB
SQL
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);
|