refactor: remove obsolete member management API module
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
-- =====================================================
|
||||
-- Atomic Course Enrollment
|
||||
--
|
||||
-- Problem: Enrolling a participant in a course requires
|
||||
-- multiple queries (check capacity, count enrolled, insert).
|
||||
-- Race conditions can over-enroll a course.
|
||||
--
|
||||
-- Fix: Single transactional PG function that locks the
|
||||
-- course row, validates capacity, and inserts with the
|
||||
-- correct status (enrolled vs waitlisted).
|
||||
-- =====================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.enroll_course_participant(
|
||||
p_course_id uuid,
|
||||
p_member_id uuid DEFAULT NULL,
|
||||
p_first_name text DEFAULT NULL,
|
||||
p_last_name text DEFAULT NULL,
|
||||
p_email text DEFAULT NULL,
|
||||
p_phone text DEFAULT NULL
|
||||
)
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = ''
|
||||
AS $$
|
||||
DECLARE
|
||||
v_course record;
|
||||
v_enrolled_count bigint;
|
||||
v_status public.enrollment_status;
|
||||
v_waitlist_position bigint;
|
||||
v_participant_id uuid;
|
||||
BEGIN
|
||||
-- 1. Lock the course row to prevent concurrent enrollment races
|
||||
SELECT * INTO v_course
|
||||
FROM public.courses
|
||||
WHERE id = p_course_id
|
||||
FOR UPDATE;
|
||||
|
||||
IF v_course IS NULL THEN
|
||||
RAISE EXCEPTION 'Course % not found', p_course_id
|
||||
USING ERRCODE = 'P0002';
|
||||
END IF;
|
||||
|
||||
-- 2. Validate course status is open for enrollment
|
||||
IF v_course.status != 'open' THEN
|
||||
RAISE EXCEPTION 'Course is not open for enrollment (current status: %)', v_course.status
|
||||
USING ERRCODE = 'P0001';
|
||||
END IF;
|
||||
|
||||
-- 3. Check registration deadline hasn't passed
|
||||
IF v_course.registration_deadline IS NOT NULL AND v_course.registration_deadline < current_date THEN
|
||||
RAISE EXCEPTION 'Registration deadline (%) has passed', v_course.registration_deadline
|
||||
USING ERRCODE = 'P0001';
|
||||
END IF;
|
||||
|
||||
-- 4. Count currently enrolled participants
|
||||
SELECT count(*) INTO v_enrolled_count
|
||||
FROM public.course_participants
|
||||
WHERE course_id = p_course_id
|
||||
AND status = 'enrolled';
|
||||
|
||||
-- 5. Determine status based on capacity
|
||||
IF v_enrolled_count >= v_course.capacity THEN
|
||||
v_status := 'waitlisted';
|
||||
ELSE
|
||||
v_status := 'enrolled';
|
||||
END IF;
|
||||
|
||||
-- 6. Insert the participant
|
||||
INSERT INTO public.course_participants (
|
||||
course_id,
|
||||
member_id,
|
||||
first_name,
|
||||
last_name,
|
||||
email,
|
||||
phone,
|
||||
status,
|
||||
enrolled_at
|
||||
) VALUES (
|
||||
p_course_id,
|
||||
p_member_id,
|
||||
p_first_name,
|
||||
p_last_name,
|
||||
p_email,
|
||||
p_phone,
|
||||
v_status,
|
||||
now()
|
||||
)
|
||||
RETURNING id INTO v_participant_id;
|
||||
|
||||
-- 7. Calculate waitlist position if waitlisted
|
||||
IF v_status = 'waitlisted' THEN
|
||||
SELECT count(*) INTO v_waitlist_position
|
||||
FROM public.course_participants
|
||||
WHERE course_id = p_course_id
|
||||
AND status = 'waitlisted';
|
||||
END IF;
|
||||
|
||||
-- 8. Return result
|
||||
RETURN jsonb_build_object(
|
||||
'participant_id', v_participant_id,
|
||||
'status', v_status::text,
|
||||
'waitlist_position', CASE WHEN v_status = 'waitlisted' THEN v_waitlist_position ELSE NULL END
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.enroll_course_participant(uuid, uuid, text, text, text, text) TO authenticated;
|
||||
GRANT EXECUTE ON FUNCTION public.enroll_course_participant(uuid, uuid, text, text, text, text) TO service_role;
|
||||
Reference in New Issue
Block a user