157 lines
6.0 KiB
PL/PgSQL
157 lines
6.0 KiB
PL/PgSQL
-- User Badges Migration for BroadcastBeat
|
|
-- Adds earned badges (Contributor, Mentor, Expert, Moderator) based on reputation and activity
|
|
|
|
-- ============================================================
|
|
-- 1. USER BADGES TABLE
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS public.user_badges (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES public.user_profiles(id) ON DELETE CASCADE,
|
|
badge_type TEXT NOT NULL CHECK (badge_type IN ('contributor', 'mentor', 'expert', 'moderator')),
|
|
awarded_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
awarded_reason TEXT
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_badges_unique
|
|
ON public.user_badges (user_id, badge_type);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_user_badges_user_id
|
|
ON public.user_badges (user_id);
|
|
|
|
-- ============================================================
|
|
-- 2. FUNCTION: EVALUATE AND AWARD BADGES
|
|
-- ============================================================
|
|
CREATE OR REPLACE FUNCTION public.evaluate_user_badges(p_user_id UUID)
|
|
RETURNS VOID
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
AS $$
|
|
DECLARE
|
|
v_reputation INTEGER;
|
|
v_thread_count INTEGER;
|
|
v_reply_count INTEGER;
|
|
v_upvotes_received INTEGER;
|
|
v_is_moderator BOOLEAN := false;
|
|
BEGIN
|
|
-- Get reputation
|
|
SELECT COALESCE(reputation, 0) INTO v_reputation
|
|
FROM public.user_profiles WHERE id = p_user_id;
|
|
|
|
-- Get thread count
|
|
SELECT COUNT(*) INTO v_thread_count
|
|
FROM public.forum_threads WHERE author_id = p_user_id;
|
|
|
|
-- Get reply count
|
|
SELECT COUNT(*) INTO v_reply_count
|
|
FROM public.forum_replies WHERE author_id = p_user_id;
|
|
|
|
-- Get total upvotes received
|
|
SELECT COALESCE(SUM(upvotes), 0) INTO v_upvotes_received
|
|
FROM (
|
|
SELECT upvotes FROM public.forum_threads WHERE author_id = p_user_id
|
|
UNION ALL
|
|
SELECT upvotes FROM public.forum_replies WHERE author_id = p_user_id
|
|
) AS all_posts;
|
|
|
|
-- Check moderator role (if user_roles table exists)
|
|
IF EXISTS (
|
|
SELECT 1 FROM information_schema.tables
|
|
WHERE table_schema = 'public' AND table_name = 'user_roles'
|
|
) THEN
|
|
SELECT EXISTS(
|
|
SELECT 1 FROM public.user_roles
|
|
WHERE user_id = p_user_id AND role IN ('moderator', 'admin')
|
|
) INTO v_is_moderator;
|
|
END IF;
|
|
|
|
-- Award CONTRIBUTOR badge: reputation >= 100 OR (threads >= 5 AND replies >= 10)
|
|
IF v_reputation >= 100 OR (v_thread_count >= 5 AND v_reply_count >= 10) THEN
|
|
INSERT INTO public.user_badges (user_id, badge_type, awarded_reason)
|
|
VALUES (p_user_id, 'contributor', 'Reached 100 reputation or posted 5+ threads and 10+ replies')
|
|
ON CONFLICT (user_id, badge_type) DO NOTHING;
|
|
END IF;
|
|
|
|
-- Award MENTOR badge: reputation >= 300 OR upvotes_received >= 50
|
|
IF v_reputation >= 300 OR v_upvotes_received >= 50 THEN
|
|
INSERT INTO public.user_badges (user_id, badge_type, awarded_reason)
|
|
VALUES (p_user_id, 'mentor', 'Reached 300 reputation or received 50+ upvotes')
|
|
ON CONFLICT (user_id, badge_type) DO NOTHING;
|
|
END IF;
|
|
|
|
-- Award EXPERT badge: reputation >= 1000 OR (upvotes_received >= 200 AND thread_count >= 20)
|
|
IF v_reputation >= 1000 OR (v_upvotes_received >= 200 AND v_thread_count >= 20) THEN
|
|
INSERT INTO public.user_badges (user_id, badge_type, awarded_reason)
|
|
VALUES (p_user_id, 'expert', 'Reached 1000 reputation or received 200+ upvotes with 20+ threads')
|
|
ON CONFLICT (user_id, badge_type) DO NOTHING;
|
|
END IF;
|
|
|
|
-- Award MODERATOR badge: has moderator/admin role
|
|
IF v_is_moderator THEN
|
|
INSERT INTO public.user_badges (user_id, badge_type, awarded_reason)
|
|
VALUES (p_user_id, 'moderator', 'Assigned moderator or admin role')
|
|
ON CONFLICT (user_id, badge_type) DO NOTHING;
|
|
END IF;
|
|
END;
|
|
$$;
|
|
|
|
-- ============================================================
|
|
-- 3. TRIGGER FUNCTION: AUTO-EVALUATE BADGES ON REPUTATION CHANGE
|
|
-- ============================================================
|
|
CREATE OR REPLACE FUNCTION public.trigger_evaluate_badges()
|
|
RETURNS TRIGGER
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
AS $$
|
|
BEGIN
|
|
PERFORM public.evaluate_user_badges(NEW.id);
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
-- ============================================================
|
|
-- 4. ENABLE RLS
|
|
-- ============================================================
|
|
ALTER TABLE public.user_badges ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- ============================================================
|
|
-- 5. RLS POLICIES
|
|
-- ============================================================
|
|
DROP POLICY IF EXISTS "public_read_user_badges" ON public.user_badges;
|
|
CREATE POLICY "public_read_user_badges"
|
|
ON public.user_badges FOR SELECT TO public USING (true);
|
|
|
|
DROP POLICY IF EXISTS "service_role_manage_user_badges" ON public.user_badges;
|
|
CREATE POLICY "service_role_manage_user_badges"
|
|
ON public.user_badges FOR ALL TO service_role USING (true) WITH CHECK (true);
|
|
|
|
-- ============================================================
|
|
-- 6. TRIGGER: EVALUATE BADGES WHEN REPUTATION CHANGES
|
|
-- ============================================================
|
|
DROP TRIGGER IF EXISTS on_reputation_change_evaluate_badges ON public.user_profiles;
|
|
CREATE TRIGGER on_reputation_change_evaluate_badges
|
|
AFTER UPDATE OF reputation ON public.user_profiles
|
|
FOR EACH ROW
|
|
WHEN (OLD.reputation IS DISTINCT FROM NEW.reputation)
|
|
EXECUTE FUNCTION public.trigger_evaluate_badges();
|
|
|
|
-- ============================================================
|
|
-- 7. BACKFILL BADGES FOR EXISTING USERS
|
|
-- ============================================================
|
|
DO $$
|
|
DECLARE
|
|
v_user_id UUID;
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM information_schema.tables
|
|
WHERE table_schema = 'public' AND table_name = 'user_profiles'
|
|
) THEN
|
|
FOR v_user_id IN SELECT id FROM public.user_profiles LOOP
|
|
PERFORM public.evaluate_user_badges(v_user_id);
|
|
END LOOP;
|
|
RAISE NOTICE 'Badge backfill completed for all existing users.';
|
|
END IF;
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
RAISE NOTICE 'Badge backfill failed: %', SQLERRM;
|
|
END $$;
|