-- 041_streaming_analytics.sql -- Analytics and User Interactions (Aligned with ORIGIN) -- === PLAYBACK HISTORY === CREATE TABLE public.playback_history ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE, -- Playback played_duration INTEGER NOT NULL, completion_percentage INTEGER NOT NULL, -- Context source VARCHAR(50), source_id UUID, device_type VARCHAR(50), -- Timestamps played_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT chk_playback_history_completion CHECK (completion_percentage >= 0 AND completion_percentage <= 100) ); CREATE INDEX idx_playback_history_user_id_played_at ON public.playback_history(user_id, played_at DESC); CREATE INDEX idx_playback_history_track_id ON public.playback_history(track_id); -- === TRACK PLAYS (Legacy - kept for Go compatibility, potentially redundant with history) === CREATE TABLE public.track_plays ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, duration INTEGER NOT NULL, played_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), device VARCHAR(100), ip_address VARCHAR(45), user_agent TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX idx_track_plays_track_id ON public.track_plays(track_id); CREATE INDEX idx_track_plays_user_id ON public.track_plays(user_id); CREATE INDEX idx_track_plays_played_at ON public.track_plays(played_at DESC); -- === TRACK LIKES === CREATE TABLE public.track_likes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_track_likes_user_track UNIQUE (user_id, track_id) ); CREATE INDEX idx_track_likes_user_id ON public.track_likes(user_id); CREATE INDEX idx_track_likes_track_id_created_at ON public.track_likes(track_id, created_at DESC); -- === TRACK COMMENTS === CREATE TABLE public.track_comments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, content TEXT NOT NULL, parent_comment_id UUID REFERENCES public.track_comments(id) ON DELETE CASCADE, timestamp_seconds INTEGER, is_edited BOOLEAN NOT NULL DEFAULT false, is_deleted BOOLEAN NOT NULL DEFAULT false, -- Legacy parent_id UUID, -- Maps to parent_comment_id created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ, CONSTRAINT chk_track_comments_content_length CHECK (LENGTH(content) >= 1 AND LENGTH(content) <= 5000) ); CREATE INDEX idx_track_comments_track_id_created_at ON public.track_comments(track_id, created_at DESC); CREATE INDEX idx_track_comments_user_id ON public.track_comments(user_id); CREATE INDEX idx_track_comments_parent_comment_id ON public.track_comments(parent_comment_id) WHERE parent_comment_id IS NOT NULL; CREATE INDEX idx_track_comments_timestamp_seconds ON public.track_comments(track_id, timestamp_seconds) WHERE timestamp_seconds IS NOT NULL; -- === TRACK SHARES (Legacy) === CREATE TABLE public.track_shares ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, share_token VARCHAR(255) NOT NULL, permissions VARCHAR(50) DEFAULT 'read', expires_at TIMESTAMPTZ, access_count BIGINT DEFAULT 0, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ, CONSTRAINT uq_track_shares_token UNIQUE (share_token) ); CREATE INDEX idx_track_shares_track_id ON public.track_shares(track_id); CREATE INDEX idx_track_shares_user_id ON public.track_shares(user_id); -- === TRACK HISTORY (Audit Log) === CREATE TABLE public.track_history ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, action VARCHAR(50) NOT NULL, old_value TEXT, new_value TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_track_history_track_id ON public.track_history(track_id); CREATE INDEX idx_track_history_action ON public.track_history(action); CREATE INDEX idx_track_history_created_at ON public.track_history(created_at DESC);