BEGIN; -- 与 delivery 侧保持同版,供 consumer/delivery 双端使用同一套 homecare foundation schema。 CREATE TABLE IF NOT EXISTS public.ec_service_requests ( id UUID PRIMARY KEY, user_id UUID NOT NULL, service_catalog_id TEXT NOT NULL DEFAULT '', service_name TEXT NOT NULL DEFAULT '', service_category TEXT NOT NULL DEFAULT '', elder_name TEXT NOT NULL DEFAULT '', elder_phone TEXT NOT NULL DEFAULT '', contact_name TEXT NOT NULL DEFAULT '', contact_phone TEXT NOT NULL DEFAULT '', address_snapshot JSONB, address_snapshot_json JSONB, scheduled_at TIMESTAMPTZ, remark TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'ORDER_CREATED', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS public.ec_care_tasks ( id UUID PRIMARY KEY, task_no TEXT NOT NULL UNIQUE, request_id UUID REFERENCES public.ec_service_requests(id) ON DELETE SET NULL, user_id UUID NOT NULL, assigned_to UUID, service_catalog_id TEXT NOT NULL DEFAULT '', service_name TEXT NOT NULL DEFAULT '', service_category TEXT NOT NULL DEFAULT '', service_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb, elder_name TEXT NOT NULL DEFAULT '', elder_phone TEXT NOT NULL DEFAULT '', contact_name TEXT NOT NULL DEFAULT '', contact_phone TEXT NOT NULL DEFAULT '', address_snapshot JSONB, address_snapshot_json JSONB, scheduled_at TIMESTAMPTZ, appointment_time TIMESTAMPTZ, remark TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'ORDER_CREATED', reject_reason TEXT NOT NULL DEFAULT '', accepted_at TIMESTAMPTZ, departed_at TIMESTAMPTZ, checked_in_at TIMESTAMPTZ, service_started_at TIMESTAMPTZ, service_completed_at TIMESTAMPTZ, acceptance_pending_at TIMESTAMPTZ, accepted_by_family_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS public.ec_care_records ( id TEXT PRIMARY KEY, task_id UUID NOT NULL REFERENCES public.ec_care_tasks(id) ON DELETE CASCADE, record_type TEXT NOT NULL DEFAULT 'service_record', care_record_type TEXT, created_by UUID, started_at TIMESTAMPTZ, finished_at TIMESTAMPTZ, checked_in_at TIMESTAMPTZ, checkin_time TIMESTAMPTZ, latitude DOUBLE PRECISION, longitude DOUBLE PRECISION, location_text TEXT NOT NULL DEFAULT '', duration_minutes INTEGER NOT NULL DEFAULT 0, actual_duration_minutes INTEGER NOT NULL DEFAULT 0, service_items_json JSONB NOT NULL DEFAULT '[]'::jsonb, service_content_json JSONB NOT NULL DEFAULT '[]'::jsonb, service_summary TEXT NOT NULL DEFAULT '', process_note TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', content TEXT NOT NULL DEFAULT '', remark TEXT NOT NULL DEFAULT '', elder_status TEXT NOT NULL DEFAULT '', health_metrics_json JSONB NOT NULL DEFAULT '{}'::jsonb, materials_used TEXT NOT NULL DEFAULT '', abnormal_note TEXT NOT NULL DEFAULT '', photos_json JSONB NOT NULL DEFAULT '[]'::jsonb, staff_remark TEXT NOT NULL DEFAULT '', family_confirmation_json JSONB NOT NULL DEFAULT '{}'::jsonb, rating INTEGER, tags_json JSONB NOT NULL DEFAULT '[]'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS public.hc_work_order_events ( id TEXT PRIMARY KEY, task_id UUID NOT NULL REFERENCES public.ec_care_tasks(id) ON DELETE CASCADE, from_status TEXT, to_status TEXT NOT NULL DEFAULT '', actor_id UUID, actor_role TEXT NOT NULL DEFAULT '', action TEXT NOT NULL DEFAULT '', remark TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS public.hc_work_order_exceptions ( id TEXT PRIMARY KEY, task_id UUID NOT NULL REFERENCES public.ec_care_tasks(id) ON DELETE CASCADE, exception_type TEXT NOT NULL DEFAULT 'other', description TEXT NOT NULL DEFAULT '', occurred_at TIMESTAMPTZ, location_text TEXT NOT NULL DEFAULT '', images_json JSONB NOT NULL DEFAULT '[]'::jsonb, need_platform_intervention BOOLEAN NOT NULL DEFAULT false, request_cancel_order BOOLEAN NOT NULL DEFAULT false, request_reschedule BOOLEAN NOT NULL DEFAULT false, created_by UUID, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS public.hc_evidence_files ( id TEXT PRIMARY KEY, task_id UUID NOT NULL REFERENCES public.ec_care_tasks(id) ON DELETE CASCADE, care_record_id TEXT, phase TEXT NOT NULL DEFAULT 'service', file_type TEXT NOT NULL DEFAULT 'image', storage_path TEXT NOT NULL DEFAULT '', file_url TEXT NOT NULL DEFAULT '', latitude DOUBLE PRECISION, longitude DOUBLE PRECISION, captured_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_ec_service_requests_user_created ON public.ec_service_requests(user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_ec_care_tasks_user_created ON public.ec_care_tasks(user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_ec_care_tasks_assigned_status ON public.ec_care_tasks(assigned_to, status, created_at DESC); CREATE INDEX IF NOT EXISTS idx_ec_care_records_task_created ON public.ec_care_records(task_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_hc_work_order_events_task_created ON public.hc_work_order_events(task_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_hc_work_order_exceptions_task_created ON public.hc_work_order_exceptions(task_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_hc_evidence_files_task_created ON public.hc_evidence_files(task_id, created_at DESC); CREATE OR REPLACE FUNCTION public.tg_ec_set_updated_at() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; DROP TRIGGER IF EXISTS trg_ec_service_requests_updated_at ON public.ec_service_requests; CREATE TRIGGER trg_ec_service_requests_updated_at BEFORE UPDATE ON public.ec_service_requests FOR EACH ROW EXECUTE FUNCTION public.tg_ec_set_updated_at(); DROP TRIGGER IF EXISTS trg_ec_care_tasks_updated_at ON public.ec_care_tasks; CREATE TRIGGER trg_ec_care_tasks_updated_at BEFORE UPDATE ON public.ec_care_tasks FOR EACH ROW EXECUTE FUNCTION public.tg_ec_set_updated_at(); DROP TRIGGER IF EXISTS trg_ec_care_records_updated_at ON public.ec_care_records; CREATE TRIGGER trg_ec_care_records_updated_at BEFORE UPDATE ON public.ec_care_records FOR EACH ROW EXECUTE FUNCTION public.tg_ec_set_updated_at(); DROP TRIGGER IF EXISTS trg_hc_work_order_exceptions_updated_at ON public.hc_work_order_exceptions; CREATE TRIGGER trg_hc_work_order_exceptions_updated_at BEFORE UPDATE ON public.hc_work_order_exceptions FOR EACH ROW EXECUTE FUNCTION public.tg_ec_set_updated_at(); ALTER TABLE public.ec_service_requests ENABLE ROW LEVEL SECURITY; ALTER TABLE public.ec_care_tasks ENABLE ROW LEVEL SECURITY; ALTER TABLE public.ec_care_records ENABLE ROW LEVEL SECURITY; ALTER TABLE public.hc_work_order_events ENABLE ROW LEVEL SECURITY; ALTER TABLE public.hc_work_order_exceptions ENABLE ROW LEVEL SECURITY; ALTER TABLE public.hc_evidence_files ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS ec_service_requests_owner_select ON public.ec_service_requests; CREATE POLICY ec_service_requests_owner_select ON public.ec_service_requests FOR SELECT TO authenticated USING (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); DROP POLICY IF EXISTS ec_service_requests_owner_insert ON public.ec_service_requests; CREATE POLICY ec_service_requests_owner_insert ON public.ec_service_requests FOR INSERT TO authenticated WITH CHECK (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); DROP POLICY IF EXISTS ec_service_requests_owner_update ON public.ec_service_requests; CREATE POLICY ec_service_requests_owner_update ON public.ec_service_requests FOR UPDATE TO authenticated USING (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())) WITH CHECK (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); DROP POLICY IF EXISTS ec_care_tasks_owner_or_staff_select ON public.ec_care_tasks; CREATE POLICY ec_care_tasks_owner_or_staff_select ON public.ec_care_tasks FOR SELECT TO authenticated USING ( user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ); DROP POLICY IF EXISTS ec_care_tasks_owner_insert ON public.ec_care_tasks; CREATE POLICY ec_care_tasks_owner_insert ON public.ec_care_tasks FOR INSERT TO authenticated WITH CHECK (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); DROP POLICY IF EXISTS ec_care_tasks_owner_or_staff_update ON public.ec_care_tasks; CREATE POLICY ec_care_tasks_owner_or_staff_update ON public.ec_care_tasks FOR UPDATE TO authenticated USING ( user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) WITH CHECK ( user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ); DROP POLICY IF EXISTS ec_care_records_task_access ON public.ec_care_records; CREATE POLICY ec_care_records_task_access ON public.ec_care_records FOR ALL TO authenticated USING ( task_id IN ( SELECT t.id FROM public.ec_care_tasks t WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) ) WITH CHECK ( task_id IN ( SELECT t.id FROM public.ec_care_tasks t WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) ); DROP POLICY IF EXISTS hc_work_order_events_task_access ON public.hc_work_order_events; CREATE POLICY hc_work_order_events_task_access ON public.hc_work_order_events FOR ALL TO authenticated USING ( task_id IN ( SELECT t.id FROM public.ec_care_tasks t WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) ) WITH CHECK ( task_id IN ( SELECT t.id FROM public.ec_care_tasks t WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) ); DROP POLICY IF EXISTS hc_work_order_exceptions_task_access ON public.hc_work_order_exceptions; CREATE POLICY hc_work_order_exceptions_task_access ON public.hc_work_order_exceptions FOR ALL TO authenticated USING ( task_id IN ( SELECT t.id FROM public.ec_care_tasks t WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) ) WITH CHECK ( task_id IN ( SELECT t.id FROM public.ec_care_tasks t WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) ); DROP POLICY IF EXISTS hc_evidence_files_task_access ON public.hc_evidence_files; CREATE POLICY hc_evidence_files_task_access ON public.hc_evidence_files FOR ALL TO authenticated USING ( task_id IN ( SELECT t.id FROM public.ec_care_tasks t WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) ) WITH CHECK ( task_id IN ( SELECT t.id FROM public.ec_care_tasks t WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) ) ); COMMIT;