-- ============================================================ -- 娓呯悊锛氬垹闄?rpc_homecare_auto_dispatch_optimized 鎵€鏈夐噸杞? -- 鏃ユ湡锛?026-06-10 -- 闂锛歅ostgREST 鎶ラ敊 PGRST203 - 鏃犳硶閫夋嫨姝g‘鐨勫嚱鏁伴噸杞? -- 鍘熷洜锛氬瓨鍦ㄤ袱涓悓鍚嶅嚱鏁帮紝鍙傛暟鍒楄〃涓嶅悓 -- 瑙e喅锛氬垹闄ゆ墍鏈夐噸杞斤紝鍙繚鐣欎慨澶嶅悗鐨勬柊鐗堟湰 -- ============================================================ -- 鏌ョ湅褰撳墠鎵€鏈夐噸杞? SELECT routine_name, argument_list, data_type FROM information_schema.routines WHERE routine_name = 'rpc_homecare_auto_dispatch_optimized'; -- 鍒犻櫎鎵€鏈夊彲鑳界殑閲嶈浇锛堟寜鍙傛暟鏁伴噺浠庡鍒板皯锛? DROP FUNCTION IF EXISTS public.rpc_homecare_auto_dispatch_optimized(TEXT, TEXT); DROP FUNCTION IF EXISTS public.rpc_homecare_auto_dispatch_optimized(TEXT); DROP FUNCTION IF EXISTS public.rpc_homecare_auto_dispatch_optimized(UUID, TEXT); DROP FUNCTION IF EXISTS public.rpc_homecare_auto_dispatch_optimized(UUID); -- 纭宸插垹闄? SELECT routine_name FROM information_schema.routines WHERE routine_name = 'rpc_homecare_auto_dispatch_optimized'; -- 搴旇杩斿洖 0 琛? -- 鐜板湪鍒涘缓淇鍚庣殑鏂扮増鏈? CREATE OR REPLACE FUNCTION public.rpc_homecare_auto_dispatch_optimized( p_order_id TEXT, p_operator_type TEXT DEFAULT 'system' ) RETURNS JSONB LANGUAGE plpgsql AS $$ DECLARE v_order RECORD; v_assignment RECORD; v_assignment_id TEXT; v_staff_id UUID; v_current_user_id UUID; v_now TIMESTAMPTZ; v_failure_code TEXT; v_failure_message TEXT; v_retryable BOOLEAN; v_status_log_id TEXT; v_attempt_log_id TEXT; v_distance_km NUMERIC; v_order_lat NUMERIC; v_order_lng NUMERIC; v_staff_lat NUMERIC; v_staff_lng NUMERIC; BEGIN v_now := now(); BEGIN v_current_user_id := gen_random_uuid(); EXCEPTION WHEN OTHERS THEN v_current_user_id := NULL; END; -- 鏌ヨ璁㈠崟淇℃伅 SELECT * INTO v_order FROM public.hss_service_orders WHERE id = p_order_id AND deleted_at IS NULL AND status = 'paid' AND dispatch_status = 'pending'; IF NOT FOUND THEN RETURN jsonb_build_object( 'success', FALSE, 'code', 'ORDER_NOT_FOUND_OR_NOT_ELIGIBLE', 'message', '璁㈠崟涓嶅瓨鍦ㄦ垨涓嶇鍚堟淳鍗曟潯浠?, 'display_type', 'toast', 'retryable', FALSE ); END IF; -- 鎻愬彇璁㈠崟鍧愭爣 v_order_lat := v_order.service_lat; v_order_lng := v_order.service_lng; IF v_order_lat IS NULL OR v_order_lng IS NULL THEN RETURN jsonb_build_object( 'success', FALSE, 'code', 'ORDER_MISSING_COORDINATES', 'message', '璁㈠崟缂哄皯浣嶇疆淇℃伅锛岃鑱旂郴瀹㈡湇', 'display_type', 'toast', 'retryable', FALSE ); END IF; -- 鏍规嵁璋冨害绔欑偣鏌ユ壘鍚堥€備汉鍛? IF v_order.dispatch_station_id IS NOT NULL THEN SELECT * INTO v_assignment FROM ( SELECT s.id AS staff_id, s.staff_name, s.online_status, s.station_id, s.latitude AS staff_lat, s.longitude AS staff_lng, distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) AS distance_km, ROW_NUMBER() OVER (ORDER BY distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) ASC) AS rn FROM public.ml_delivery_staff s WHERE s.station_id::TEXT = v_order.dispatch_station_id AND s.online_status = 'online' AND s.latitude IS NOT NULL AND s.longitude IS NOT NULL AND ( v_order.required_qualification_code IS NULL OR EXISTS ( SELECT 1 FROM public.hc_worker_qualifications q WHERE q.staff_id::TEXT = s.id::TEXT AND q.qualification_code = v_order.required_qualification_code AND q.deleted_at IS NULL AND q.status::TEXT IN ('1', 'active', 'approved', 'valid') AND (q.valid_from IS NULL OR q.valid_from <= v_now) AND (q.valid_until IS NULL OR q.valid_until >= v_now) ) ) AND distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) <= COALESCE(s.dispatch_radius_km, 20) AND NOT EXISTS ( SELECT 1 FROM public.hss_service_assignments sa WHERE sa.staff_id = s.id AND sa.status IN ('assigned', 'accepted', 'in_progress') AND sa.deleted_at IS NULL AND sa.start_time <= v_now AND sa.end_time > v_now ) ) AS ranked_candidates WHERE rn = 1 LIMIT 1; ELSE SELECT * INTO v_assignment FROM ( SELECT s.id AS staff_id, s.staff_name, s.online_status, s.station_id, s.latitude AS staff_lat, s.longitude AS staff_lng, distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) AS distance_km, ROW_NUMBER() OVER (ORDER BY distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) ASC) AS rn FROM public.ml_delivery_staff s WHERE s.online_status = 'online' AND s.latitude IS NOT NULL AND s.longitude IS NOT NULL AND ( v_order.dispatch_station_id IS NULL OR s.station_id::TEXT = v_order.dispatch_station_id ) AND ( v_order.required_qualification_code IS NULL OR EXISTS ( SELECT 1 FROM public.hc_worker_qualifications q WHERE q.staff_id::TEXT = s.id::TEXT AND q.qualification_code = v_order.required_qualification_code AND q.deleted_at IS NULL AND q.status::TEXT IN ('1', 'active', 'approved', 'valid') AND (q.valid_from IS NULL OR q.valid_from <= v_now) AND (q.valid_until IS NULL OR q.valid_until >= v_now) ) ) AND distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) <= COALESCE(s.dispatch_radius_km, 20) AND NOT EXISTS ( SELECT 1 FROM public.hss_service_assignments sa WHERE sa.staff_id = s.id AND sa.status IN ('assigned', 'accepted', 'in_progress') AND sa.deleted_at IS NULL AND sa.start_time <= v_now AND sa.end_time > v_now ) ) AS ranked_candidates WHERE rn = 1 LIMIT 1; END IF; -- 濡傛灉鎵惧埌鍚堥€傜殑浜哄憳锛屽垱寤哄垎閰? IF FOUND AND v_assignment.staff_id IS NOT NULL THEN v_staff_id := v_assignment.staff_id; v_assignment_id := 'asgn-' || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT || '-' || upper(substr(md5(p_order_id || ':' || v_staff_id::TEXT), 1, 10)); -- 鍒涘缓鍒嗛厤璁板綍 INSERT INTO public.hss_service_assignments ( id, order_id, staff_id, status, assigned_at, start_time, end_time, created_at ) VALUES ( v_assignment_id, p_order_id, v_staff_id, 'assigned', v_now, v_now, v_now + INTERVAL '2 hours', v_now ); -- 鏇存柊璁㈠崟鐘舵€? UPDATE public.hss_service_orders SET dispatch_status = 'assigned', current_staff_id = v_staff_id, assigned_at = v_now, updated_at = v_now WHERE id = p_order_id; -- 璁板綍鐘舵€佹棩蹇楋紙浣跨敤姝g‘鐨?operator_role 瀛楁锛? v_status_log_id := 'slg-' || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT || '-' || upper(substr(md5(p_order_id || ':assigned'), 1, 10)); INSERT INTO public.hss_service_order_status_logs ( id, order_id, from_status, to_status, operator_role, operator_id, remark, created_at ) VALUES ( v_status_log_id, p_order_id, 'created', 'assigned', 'system', v_current_user_id, '绯荤粺鑷姩娲惧崟', v_now ); -- 璁板綍娲惧崟灏濊瘯鏃ュ織 v_attempt_log_id := 'dalog-' || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT || '-' || upper(substr(md5(p_order_id || ':attempt'), 1, 10)); INSERT INTO public.hss_service_dispatch_attempt_logs ( id, order_id, requested_by_user_id, selected_staff_id, selected_station_id, success, assigned_at, created_at ) VALUES ( v_attempt_log_id, p_order_id, v_current_user_id, v_staff_id, v_assignment.station_id::TEXT, TRUE, v_now, v_now ); RETURN jsonb_build_object( 'success', TRUE, 'code', 'DISPATCH_ASSIGNED', 'message', '绯荤粺宸蹭负鎮ㄥ尮閰嶆湇鍔′汉鍛?, 'display_type', 'none', 'retryable', FALSE, 'dispatch_status', 'assigned', 'order_id', p_order_id, 'assignment_id', v_assignment_id, 'staff_name', v_assignment.staff_name, 'distance_km', v_assignment.distance_km ); END IF; -- 鏈壘鍒板悎閫備汉鍛橈紝鍒嗘瀽澶辫触鍘熷洜 v_failure_code := 'NO_ONLINE_STAFF'; v_failure_message := '鏆傛棤鍦ㄧ嚎鏈嶅姟浜哄憳锛岃绋嶅悗閲嶈瘯'; v_retryable := TRUE; -- 妫€鏌ユ槸鍚︽湁鍦ㄧ嚎浜哄憳 IF NOT EXISTS ( SELECT 1 FROM public.ml_delivery_staff WHERE online_status = 'online' AND deleted_at IS NULL AND latitude IS NOT NULL AND longitude IS NOT NULL ) THEN v_failure_code := 'NO_ONLINE_STAFF'; v_failure_message := '鏆傛棤鍦ㄧ嚎浜哄憳锛岃绋嶅悗閲嶈瘯鎴栬仈绯诲鏈?; -- 妫€鏌ユ槸鍚︽湁绔欑偣鍐呯殑鍦ㄧ嚎浜哄憳 ELSIF v_order.dispatch_station_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM public.ml_delivery_staff s WHERE s.station_id::TEXT = v_order.dispatch_station_id AND s.online_status = 'online' AND s.deleted_at IS NULL ) THEN v_failure_code := 'NO_STAFF_IN_SERVICE_STATION'; v_failure_message := '褰撳墠鍖哄煙鏆傛棤浜哄憳锛岃绋嶅悗閲嶈瘯鎴栬仈绯诲鏈?; -- 妫€鏌ユ槸鍚︽湁鍏峰璧勮川鐨勪汉鍛? ELSIF v_order.required_qualification_code IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM public.hc_worker_qualifications q WHERE q.qualification_code = v_order.required_qualification_code AND q.deleted_at IS NULL AND q.status::TEXT IN ('1', 'active', 'approved', 'valid') AND (q.valid_from IS NULL OR q.valid_from <= v_now) AND (q.valid_until IS NULL OR q.valid_until >= v_now) ) THEN v_failure_code := 'NO_QUALIFIED_STAFF'; v_failure_message := '鏆傛棤鍏峰璇ヨ祫璐ㄧ殑鏈嶅姟浜哄憳锛岃绋嶅悗閲嶈瘯鎴栬仈绯诲鏈?; -- 妫€鏌ラ檮杩戞槸鍚︽湁浜哄憳 ELSIF NOT EXISTS ( SELECT 1 FROM public.ml_delivery_staff s WHERE s.online_status = 'online' AND s.latitude IS NOT NULL AND s.longitude IS NOT NULL AND distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) <= COALESCE(s.dispatch_radius_km, 20) ) THEN v_failure_code := 'NO_NEARBY_STAFF'; v_failure_message := '闄勮繎鏆傛棤鍙笂闂ㄦ湇鍔′汉鍛橈紝璇风◢鍚庨噸璇曟垨鑱旂郴瀹㈡湇'; -- 鎵€鏈夌鍚堟潯浠剁殑浜哄憳閮藉湪蹇? ELSIF EXISTS ( SELECT 1 FROM public.ml_delivery_staff s WHERE s.online_status = 'online' AND s.latitude IS NOT NULL AND s.longitude IS NOT NULL AND distance_calc.distance_km(s.latitude, s.longitude, v_order_lat, v_order_lng) <= COALESCE(s.dispatch_radius_km, 20) AND EXISTS ( SELECT 1 FROM public.hss_service_assignments sa WHERE sa.staff_id = s.id AND sa.status IN ('assigned', 'accepted', 'in_progress') AND sa.deleted_at IS NULL AND sa.start_time <= v_now AND sa.end_time > v_now ) ) THEN v_failure_code := 'ALL_ELIGIBLE_STAFF_BUSY'; v_failure_message := '鍙湇鍔′汉鍛樺潎鍦ㄥ繖锛岃绋嶅悗閲嶆柊娲惧崟'; END IF; -- 鏇存柊璁㈠崟涓哄け璐ョ姸鎬? UPDATE public.hss_service_orders SET dispatch_status = 'failed', dispatch_error_code = v_failure_code, dispatch_error_message = v_failure_message, dispatch_failed_at = v_now, updated_at = v_now WHERE id = p_order_id; -- 璁板綍澶辫触鏃ュ織 v_attempt_log_id := 'dalog-' || floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT || '-' || upper(substr(md5(p_order_id || ':failed'), 1, 10)); INSERT INTO public.hss_service_dispatch_attempt_logs ( id, order_id, requested_by_user_id, selected_staff_id, selected_station_id, success, failure_code, failure_message, retryable, created_at ) VALUES ( v_attempt_log_id, p_order_id, v_current_user_id, NULL, NULL, FALSE, v_failure_code, v_failure_message, v_retryable, v_now ); RETURN jsonb_build_object( 'success', FALSE, 'code', v_failure_code, 'message', v_failure_message, 'display_type', 'modal', 'retryable', v_retryable, 'dispatch_status', 'failed', 'order_id', p_order_id ); END; $$; COMMENT ON FUNCTION public.rpc_homecare_auto_dispatch_optimized IS '浼樺寲鐗堝眳瀹舵湇鍔¤嚜鍔ㄦ淳鍗昍PC - 娓呯悊鎵€鏈夐噸杞藉悗鍙繚鐣欐鐗堟湰';