BEGIN; ALTER TABLE public.ml_user_addresses ADD COLUMN IF NOT EXISTS latitude DOUBLE PRECISION, ADD COLUMN IF NOT EXISTS longitude DOUBLE PRECISION, ADD COLUMN IF NOT EXISTS coordinate_type TEXT NOT NULL DEFAULT 'gcj02'; CREATE TABLE IF NOT EXISTS public.hss_service_orders ( id TEXT PRIMARY KEY, order_no TEXT NOT NULL UNIQUE, user_id UUID NOT NULL, service_id TEXT NOT NULL, service_name TEXT NOT NULL, service_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb, service_address_id UUID, address_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb, recipient_name TEXT NOT NULL DEFAULT '', recipient_phone TEXT NOT NULL DEFAULT '', contact_name TEXT NOT NULL DEFAULT '', contact_phone TEXT NOT NULL DEFAULT '', appointment_time TIMESTAMPTZ, remark TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'created', current_assignment_id TEXT NOT NULL DEFAULT '', current_staff_id UUID, accepted_at TIMESTAMPTZ, departed_at TIMESTAMPTZ, arrived_at TIMESTAMPTZ, service_started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ, pending_acceptance_at TIMESTAMPTZ, accepted_by_user_at TIMESTAMPTZ, reviewed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ, deleted_by UUID, CONSTRAINT chk_hss_service_orders_status CHECK ( status IN ( 'created', 'paid', 'assigned', 'accepted', 'rejected', 'departed', 'arrived', 'in_service', 'completed', 'pending_acceptance', 'accepted_by_user', 'reviewed', 'settled', 'cancelled', 'exception' ) ) ); CREATE TABLE IF NOT EXISTS public.hss_service_assignments ( id TEXT PRIMARY KEY, order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE, staff_id UUID NOT NULL REFERENCES public.ml_delivery_staff(id) ON DELETE RESTRICT, station_id UUID REFERENCES public.ml_delivery_stations(id) ON DELETE SET NULL, status TEXT NOT NULL DEFAULT 'assigned', assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(), accepted_at TIMESTAMPTZ, rejected_at TIMESTAMPTZ, reject_reason TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT chk_hss_service_assignments_status CHECK ( status IN ('assigned', 'accepted', 'rejected', 'departed', 'arrived', 'in_service', 'pending_acceptance', 'completed', 'cancelled', 'exception') ) ); CREATE TABLE IF NOT EXISTS public.hss_service_execution_records ( id TEXT PRIMARY KEY, order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE, assignment_id TEXT NOT NULL REFERENCES public.hss_service_assignments(id) ON DELETE CASCADE, checkin_time TIMESTAMPTZ, checkin_latitude DOUBLE PRECISION, checkin_longitude DOUBLE PRECISION, checkin_address TEXT NOT NULL DEFAULT '', service_started_at TIMESTAMPTZ, service_finished_at TIMESTAMPTZ, actual_duration_minutes INTEGER NOT NULL DEFAULT 0, service_items_json JSONB NOT NULL DEFAULT '[]'::jsonb, summary TEXT NOT NULL DEFAULT '', remark TEXT NOT NULL DEFAULT '', track_points_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.hss_service_evidence_files ( id TEXT PRIMARY KEY, order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE, execution_record_id TEXT REFERENCES public.hss_service_execution_records(id) ON DELETE CASCADE, 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 TABLE IF NOT EXISTS public.hss_service_order_status_logs ( id TEXT PRIMARY KEY, order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE, from_status TEXT NOT NULL DEFAULT '', to_status TEXT NOT NULL, operator_id UUID, operator_role TEXT NOT NULL DEFAULT '', remark TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS public.hss_service_reviews ( id TEXT PRIMARY KEY, order_id TEXT NOT NULL REFERENCES public.hss_service_orders(id) ON DELETE CASCADE, user_id UUID NOT NULL, rating INTEGER NOT NULL DEFAULT 5, tags_json JSONB NOT NULL DEFAULT '[]'::jsonb, content TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_hss_service_orders_user_status ON public.hss_service_orders(user_id, status) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_hss_service_orders_staff_status ON public.hss_service_orders(current_staff_id, status) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_hss_service_assignments_staff_status ON public.hss_service_assignments(staff_id, status); CREATE INDEX IF NOT EXISTS idx_hss_service_execution_records_order ON public.hss_service_execution_records(order_id); CREATE INDEX IF NOT EXISTS idx_hss_service_evidence_files_order ON public.hss_service_evidence_files(order_id); CREATE INDEX IF NOT EXISTS idx_hss_service_logs_order_created ON public.hss_service_order_status_logs(order_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_hss_service_reviews_order ON public.hss_service_reviews(order_id); CREATE OR REPLACE FUNCTION public.tg_hss_set_updated_at() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; DROP TRIGGER IF EXISTS trg_hss_service_orders_updated_at ON public.hss_service_orders; CREATE TRIGGER trg_hss_service_orders_updated_at BEFORE UPDATE ON public.hss_service_orders FOR EACH ROW EXECUTE FUNCTION public.tg_hss_set_updated_at(); DROP TRIGGER IF EXISTS trg_hss_service_assignments_updated_at ON public.hss_service_assignments; CREATE TRIGGER trg_hss_service_assignments_updated_at BEFORE UPDATE ON public.hss_service_assignments FOR EACH ROW EXECUTE FUNCTION public.tg_hss_set_updated_at(); DROP TRIGGER IF EXISTS trg_hss_service_execution_records_updated_at ON public.hss_service_execution_records; CREATE TRIGGER trg_hss_service_execution_records_updated_at BEFORE UPDATE ON public.hss_service_execution_records FOR EACH ROW EXECUTE FUNCTION public.tg_hss_set_updated_at(); ALTER TABLE public.hss_service_orders ENABLE ROW LEVEL SECURITY; ALTER TABLE public.hss_service_assignments ENABLE ROW LEVEL SECURITY; ALTER TABLE public.hss_service_execution_records ENABLE ROW LEVEL SECURITY; ALTER TABLE public.hss_service_evidence_files ENABLE ROW LEVEL SECURITY; ALTER TABLE public.hss_service_order_status_logs ENABLE ROW LEVEL SECURITY; ALTER TABLE public.hss_service_reviews ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS hss_service_orders_user_select ON public.hss_service_orders; CREATE POLICY hss_service_orders_user_select ON public.hss_service_orders FOR SELECT TO authenticated USING ( deleted_at IS NULL AND ( user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR current_staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL) ) ); DROP POLICY IF EXISTS hss_service_orders_user_insert ON public.hss_service_orders; CREATE POLICY hss_service_orders_user_insert ON public.hss_service_orders FOR INSERT TO authenticated WITH CHECK (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); DROP POLICY IF EXISTS hss_service_orders_user_update ON public.hss_service_orders; CREATE POLICY hss_service_orders_user_update ON public.hss_service_orders FOR UPDATE TO authenticated USING ( deleted_at IS NULL AND ( user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR current_staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL) ) ) WITH CHECK ( deleted_at IS NULL AND ( user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) OR current_staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL) ) ); DROP POLICY IF EXISTS hss_service_assignments_staff_select ON public.hss_service_assignments; CREATE POLICY hss_service_assignments_staff_select ON public.hss_service_assignments FOR SELECT TO authenticated USING (staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL)); DROP POLICY IF EXISTS hss_service_assignments_staff_update ON public.hss_service_assignments; CREATE POLICY hss_service_assignments_staff_update ON public.hss_service_assignments FOR UPDATE TO authenticated USING (staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL)) WITH CHECK (staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL)); DROP POLICY IF EXISTS hss_service_assignments_staff_insert ON public.hss_service_assignments; CREATE POLICY hss_service_assignments_staff_insert ON public.hss_service_assignments FOR INSERT TO authenticated WITH CHECK (staff_id IN (SELECT id FROM public.ml_delivery_staff WHERE uid IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) AND deleted_at IS NULL)); DROP POLICY IF EXISTS hss_service_execution_records_order_access ON public.hss_service_execution_records; CREATE POLICY hss_service_execution_records_order_access ON public.hss_service_execution_records FOR ALL TO authenticated USING (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL)) WITH CHECK (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL)); DROP POLICY IF EXISTS hss_service_evidence_files_order_access ON public.hss_service_evidence_files; CREATE POLICY hss_service_evidence_files_order_access ON public.hss_service_evidence_files FOR ALL TO authenticated USING (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL)) WITH CHECK (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL)); DROP POLICY IF EXISTS hss_service_order_status_logs_order_access ON public.hss_service_order_status_logs; CREATE POLICY hss_service_order_status_logs_order_access ON public.hss_service_order_status_logs FOR ALL TO authenticated USING (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL)) WITH CHECK (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL)); DROP POLICY IF EXISTS hss_service_reviews_order_access ON public.hss_service_reviews; CREATE POLICY hss_service_reviews_order_access ON public.hss_service_reviews FOR ALL TO authenticated USING (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL)) WITH CHECK (order_id IN (SELECT id FROM public.hss_service_orders WHERE deleted_at IS NULL)); COMMENT ON TABLE public.hss_service_orders IS '居家上门服务订单主表'; COMMENT ON TABLE public.hss_service_assignments IS '居家上门服务派单表'; COMMENT ON TABLE public.hss_service_execution_records IS '居家上门服务执行记录表'; COMMENT ON TABLE public.hss_service_evidence_files IS '居家上门服务证据文件表'; COMMENT ON TABLE public.hss_service_order_status_logs IS '居家上门服务状态日志表'; COMMENT ON TABLE public.hss_service_reviews IS '居家上门服务评价表'; COMMIT;