it0/packages/shared/database/migrations/009-create-notification-cha...

54 lines
2.6 KiB
SQL

-- ============================================================
-- Migration 009: Notification Channels + User Preferences
-- Phase 2: Channel-based opt-out system
-- ============================================================
-- 1. Notification channels (platform-managed)
-- Platform defines channels like "billing", "security", "marketing"
-- Users can opt out of non-mandatory channels
CREATE TABLE IF NOT EXISTS public.notification_channels (
channel_key VARCHAR(50) PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
is_mandatory BOOLEAN NOT NULL DEFAULT false,
-- mandatory=true: users cannot opt out (e.g. security alerts)
-- mandatory=false: users can disable (e.g. marketing)
is_enabled BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Seed default channels
INSERT INTO public.notification_channels (channel_key, name, description, is_mandatory) VALUES
('system', '系统通知', '平台级系统公告与维护通知', true),
('security', '安全告警', '账号安全、异常登录等安全相关通知', true),
('billing', '账单通知', '订阅续费、发票、扣款通知', false),
('feature', '新功能', '产品新功能与版本更新通知', false),
('marketing', '营销推广', '促销活动、优惠券、活动邀请', false),
('ops', '运维报警', '服务器异常、告警触发通知', false)
ON CONFLICT (channel_key) DO NOTHING;
-- 2. User notification preferences (per-user opt-out per channel)
-- Only non-mandatory channels can be opted out
CREATE TABLE IF NOT EXISTS public.user_notification_preferences (
user_id VARCHAR(100) NOT NULL,
channel_key VARCHAR(50) NOT NULL REFERENCES public.notification_channels(channel_key) ON DELETE CASCADE,
enabled BOOLEAN NOT NULL DEFAULT true,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (user_id, channel_key)
);
CREATE INDEX IF NOT EXISTS idx_unp_user ON public.user_notification_preferences (user_id);
-- 3. Notification segment members (for BY_SEGMENT targeting, Phase 4)
-- Platform populates this table via ETL/cron jobs
CREATE TABLE IF NOT EXISTS public.notification_segment_members (
segment_key VARCHAR(100) NOT NULL,
tenant_id VARCHAR(100) NOT NULL,
synced_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (segment_key, tenant_id)
);
CREATE INDEX IF NOT EXISTS idx_nsm_segment ON public.notification_segment_members (segment_key);
CREATE INDEX IF NOT EXISTS idx_nsm_tenant ON public.notification_segment_members (tenant_id);