Fix push notifications never delivered — 3 root causes

1. CRITICAL: Double idempotency check silently discarded every notification.
   process_scheduled_notifications called try_mark_notification_pushed first,
   then send_fcm called it again and saw "already pushed" → skipped FCM send.
   Fix: Remove the check from process_scheduled_notifications; send_fcm's
   own check is sufficient (and needed for direct Flutter client calls).

2. SEND_FCM_URL env var crash: If not configured, edge function crashed on
   module load. Fix: Derive from SUPABASE_URL (always auto-set) as fallback.

3. Timing windows too narrow: ±30s windows with 60s cron intervals could
   miss events between runs. Fix: Widen to ±90s. ON CONFLICT DO NOTHING
   prevents duplicates, making wider windows safe.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 20:50:45 +08:00
parent d81e2cde26
commit 758869920c
2 changed files with 15 additions and 24 deletions
@@ -71,8 +71,8 @@ BEGIN
FOR rec IN
SELECT id AS schedule_id, 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'
WHERE start_time BETWEEN now() + interval '15 minutes' - interval '90 seconds'
AND now() + interval '15 minutes' + interval '90 seconds'
AND status = 'active'
LOOP
-- Skip if user already checked in for this duty
@@ -92,7 +92,7 @@ BEGIN
FOR rec IN
SELECT id AS schedule_id, user_id, end_time AS end_at
FROM public.duty_schedules
WHERE end_time BETWEEN now() - interval '30 seconds' AND now() + interval '30 seconds'
WHERE end_time BETWEEN now() - interval '90 seconds' AND now() + interval '90 seconds'
AND status IN ('arrival', 'late')
LOOP
INSERT INTO public.scheduled_notifications (schedule_id, user_id, notify_type, scheduled_for)
@@ -235,8 +235,8 @@ BEGIN
JOIN public.it_service_request_assignments isra ON isra.request_id = isr.id
WHERE isr.status IN ('scheduled', 'in_progress_dry_run')
AND isr.event_date IS NOT NULL
AND isr.event_date BETWEEN now() + interval '60 minutes' - interval '30 seconds'
AND now() + interval '60 minutes' + interval '30 seconds'
AND isr.event_date BETWEEN now() + interval '60 minutes' - interval '90 seconds'
AND now() + interval '60 minutes' + interval '90 seconds'
LOOP
INSERT INTO public.scheduled_notifications
(it_service_request_id, user_id, notify_type, scheduled_for)
@@ -348,8 +348,8 @@ BEGIN
FOR rec IN
SELECT ds.id AS schedule_id, ds.user_id, ds.end_time
FROM public.duty_schedules ds
WHERE ds.end_time BETWEEN now() + interval '15 minutes' - interval '30 seconds'
AND now() + interval '15 minutes' + interval '30 seconds'
WHERE ds.end_time BETWEEN now() + interval '15 minutes' - interval '90 seconds'
AND now() + interval '15 minutes' + interval '90 seconds'
AND ds.status IN ('arrival', 'late')
-- User has pending (non-paused) tasks
AND EXISTS (
@@ -395,7 +395,7 @@ BEGIN
AND ps.slip_end IS NULL
AND ps.slip_start IS NOT NULL
AND ps.slip_start + interval '45 minutes'
BETWEEN now() - interval '30 seconds' AND now() + interval '30 seconds'
BETWEEN now() - interval '90 seconds' AND now() + interval '90 seconds'
ON CONFLICT DO NOTHING;
END;
$$;