Initial commit for shift push notification reminders
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
-- Migration: add scheduled_notifications queue and enqueue function for shift reminders
|
||||
-- Creates a queue table and stored procedure to enqueue 15-minute and end-of-shift reminders
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
-- Queue table for scheduled notifications
|
||||
CREATE TABLE IF NOT EXISTS public.scheduled_notifications (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
schedule_id uuid NOT NULL REFERENCES public.duty_schedules(id) ON DELETE CASCADE,
|
||||
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
notify_type text NOT NULL, -- 'start_15' | 'end'
|
||||
scheduled_for timestamptz NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
processed boolean NOT NULL DEFAULT false,
|
||||
processed_at timestamptz,
|
||||
retry_count int NOT NULL DEFAULT 0,
|
||||
last_error text
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_scheduled_for ON public.scheduled_notifications(scheduled_for);
|
||||
CREATE INDEX IF NOT EXISTS idx_scheduled_notifications_processed ON public.scheduled_notifications(processed);
|
||||
|
||||
-- Ensure uniqueness per schedule/user/notify type
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint c
|
||||
JOIN pg_class t ON c.conrelid = t.oid
|
||||
WHERE c.conname = 'uniq_sched_user_type' AND t.relname = 'scheduled_notifications'
|
||||
) THEN
|
||||
ALTER TABLE public.scheduled_notifications ADD CONSTRAINT uniq_sched_user_type UNIQUE (schedule_id, user_id, notify_type);
|
||||
END IF;
|
||||
END$$;
|
||||
|
||||
-- The project already has notification_pushes and try_mark_notification_pushed (see earlier migrations).
|
||||
|
||||
-- Enqueue function: finds duty_schedules in the configured windows and inserts into queue.
|
||||
CREATE OR REPLACE FUNCTION public.enqueue_due_shift_notifications()
|
||||
RETURNS void LANGUAGE plpgsql AS $$
|
||||
DECLARE
|
||||
rec RECORD;
|
||||
BEGIN
|
||||
-- 15-minute-before reminders
|
||||
FOR rec IN
|
||||
SELECT id AS schedule_id, user_id AS user_id, start_time AS start_at
|
||||
FROM public.duty_schedules
|
||||
WHERE start_time BETWEEN now() + interval '15 minutes' - interval '30 seconds' AND now() + interval '15 minutes' + interval '30 seconds'
|
||||
AND status = 'active'
|
||||
LOOP
|
||||
-- Skip if user already checked in for this duty (attendance_logs references duty_schedule_id)
|
||||
IF EXISTS (SELECT 1 FROM public.attendance_logs al WHERE al.duty_schedule_id = rec.schedule_id AND al.check_in_at IS NOT NULL) THEN
|
||||
CONTINUE;
|
||||
END IF;
|
||||
|
||||
INSERT INTO public.scheduled_notifications (schedule_id, user_id, notify_type, scheduled_for)
|
||||
VALUES (rec.schedule_id, rec.user_id, 'start_15', rec.start_at)
|
||||
ON CONFLICT (schedule_id, user_id, notify_type) DO NOTHING;
|
||||
END LOOP;
|
||||
|
||||
-- End-of-shift reminders (near exact end)
|
||||
FOR rec IN
|
||||
SELECT id AS schedule_id, user_id AS user_id, end_time AS end_at
|
||||
FROM public.duty_schedules
|
||||
WHERE end_time BETWEEN now() - interval '30 seconds' AND now() + interval '30 seconds'
|
||||
AND status = 'active'
|
||||
LOOP
|
||||
INSERT INTO public.scheduled_notifications (schedule_id, user_id, notify_type, scheduled_for)
|
||||
VALUES (rec.schedule_id, rec.user_id, 'end', rec.end_at)
|
||||
ON CONFLICT (schedule_id, user_id, notify_type) DO NOTHING;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- NOTE: Schedule this function with pg_cron or your Postgres scheduler. Example (requires pg_cron extension enabled):
|
||||
-- SELECT cron.schedule('shift_reminders_every_min', '*/1 * * * *', $$SELECT public.enqueue_due_shift_notifications();$$);
|
||||
Reference in New Issue
Block a user